Completed
Pull Request — master (#8061)
by Morris
109:58 queued 95:16
created
lib/public/SystemTag/ISystemTagManager.php 2 patches
Doc Comments   +7 added lines, -4 removed lines patch added patch discarded remove patch
@@ -102,17 +102,19 @@  discard block
 block discarded – undo
102 102
 	 * with the same attributes
103 103
 	 *
104 104
 	 * @since 9.0.0
105
+	 * @return void
105 106
 	 */
106 107
 	public function updateTag($tagId, $newName, $userVisible, $userAssignable);
107 108
 
108 109
 	/**
109 110
 	 * Delete the given tags from the database and all their relationships.
110 111
 	 *
111
-	 * @param string|array $tagIds array of tag ids
112
+	 * @param string $tagIds array of tag ids
112 113
 	 *
113 114
 	 * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist
114 115
 	 *
115 116
 	 * @since 9.0.0
117
+	 * @return void
116 118
 	 */
117 119
 	public function deleteTags($tagIds);
118 120
 
@@ -123,7 +125,7 @@  discard block
 block discarded – undo
123 125
 	 * @param ISystemTag $tag tag to check permission for
124 126
 	 * @param IUser $user user to check permission for
125 127
 	 *
126
-	 * @return true if the user is allowed to assign/unassign the tag, false otherwise
128
+	 * @return boolean if the user is allowed to assign/unassign the tag, false otherwise
127 129
 	 *
128 130
 	 * @since 9.1.0
129 131
 	 */
@@ -133,9 +135,9 @@  discard block
 block discarded – undo
133 135
 	 * Checks whether the given user is allowed to see the tag with the given id.
134 136
 	 *
135 137
 	 * @param ISystemTag $tag tag to check permission for
136
-	 * @param IUser $user user to check permission for
138
+	 * @param IUser $userId user to check permission for
137 139
 	 *
138
-	 * @return true if the user can see the tag, false otherwise
140
+	 * @return boolean if the user can see the tag, false otherwise
139 141
 	 *
140 142
 	 * @since 9.1.0
141 143
 	 */
@@ -148,6 +150,7 @@  discard block
 block discarded – undo
148 150
 	 * @param string[] $groupIds group ids of groups that can assign/unassign the tag
149 151
 	 *
150 152
 	 * @since 9.1.0
153
+	 * @return void
151 154
 	 */
152 155
 	public function setTagGroups(ISystemTag $tag, $groupIds);
153 156
 
Please login to merge, or discard this patch.
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -33,133 +33,133 @@
 block discarded – undo
33 33
  */
34 34
 interface ISystemTagManager {
35 35
 
36
-	/**
37
-	 * Returns the tag objects matching the given tag ids.
38
-	 *
39
-	 * @param array|string $tagIds id or array of unique ids of the tag to retrieve
40
-	 *
41
-	 * @return \OCP\SystemTag\ISystemTag[] array of system tags with tag id as key
42
-	 *
43
-	 * @throws \InvalidArgumentException if at least one given tag ids is invalid (string instead of integer, etc.)
44
-	 * @throws \OCP\SystemTag\TagNotFoundException if at least one given tag ids did no exist
45
-	 * 			The message contains a json_encoded array of the ids that could not be found
46
-	 *
47
-	 * @since 9.0.0
48
-	 */
49
-	public function getTagsByIds($tagIds);
36
+    /**
37
+     * Returns the tag objects matching the given tag ids.
38
+     *
39
+     * @param array|string $tagIds id or array of unique ids of the tag to retrieve
40
+     *
41
+     * @return \OCP\SystemTag\ISystemTag[] array of system tags with tag id as key
42
+     *
43
+     * @throws \InvalidArgumentException if at least one given tag ids is invalid (string instead of integer, etc.)
44
+     * @throws \OCP\SystemTag\TagNotFoundException if at least one given tag ids did no exist
45
+     * 			The message contains a json_encoded array of the ids that could not be found
46
+     *
47
+     * @since 9.0.0
48
+     */
49
+    public function getTagsByIds($tagIds);
50 50
 
51
-	/**
52
-	 * Returns the tag object matching the given attributes.
53
-	 *
54
-	 * @param string $tagName tag name
55
-	 * @param bool $userVisible whether the tag is visible by users
56
-	 * @param bool $userAssignable whether the tag is assignable by users
57
-	 *
58
-	 * @return \OCP\SystemTag\ISystemTag system tag
59
-	 *
60
-	 * @throws \OCP\SystemTag\TagNotFoundException if tag does not exist
61
-	 *
62
-	 * @since 9.0.0
63
-	 */
64
-	public function getTag($tagName, $userVisible, $userAssignable);
51
+    /**
52
+     * Returns the tag object matching the given attributes.
53
+     *
54
+     * @param string $tagName tag name
55
+     * @param bool $userVisible whether the tag is visible by users
56
+     * @param bool $userAssignable whether the tag is assignable by users
57
+     *
58
+     * @return \OCP\SystemTag\ISystemTag system tag
59
+     *
60
+     * @throws \OCP\SystemTag\TagNotFoundException if tag does not exist
61
+     *
62
+     * @since 9.0.0
63
+     */
64
+    public function getTag($tagName, $userVisible, $userAssignable);
65 65
 
66
-	/**
67
-	 * Creates the tag object using the given attributes.
68
-	 *
69
-	 * @param string $tagName tag name
70
-	 * @param bool $userVisible whether the tag is visible by users
71
-	 * @param bool $userAssignable whether the tag is assignable by users
72
-	 *
73
-	 * @return \OCP\SystemTag\ISystemTag system tag
74
-	 *
75
-	 * @throws \OCP\SystemTag\TagAlreadyExistsException if tag already exists
76
-	 *
77
-	 * @since 9.0.0
78
-	 */
79
-	public function createTag($tagName, $userVisible, $userAssignable);
66
+    /**
67
+     * Creates the tag object using the given attributes.
68
+     *
69
+     * @param string $tagName tag name
70
+     * @param bool $userVisible whether the tag is visible by users
71
+     * @param bool $userAssignable whether the tag is assignable by users
72
+     *
73
+     * @return \OCP\SystemTag\ISystemTag system tag
74
+     *
75
+     * @throws \OCP\SystemTag\TagAlreadyExistsException if tag already exists
76
+     *
77
+     * @since 9.0.0
78
+     */
79
+    public function createTag($tagName, $userVisible, $userAssignable);
80 80
 
81
-	/**
82
-	 * Returns all known tags, optionally filtered by visibility.
83
-	 *
84
-	 * @param bool|null $visibilityFilter filter by visibility if non-null
85
-	 * @param string $nameSearchPattern optional search pattern for the tag name
86
-	 *
87
-	 * @return \OCP\SystemTag\ISystemTag[] array of system tags or empty array if none found
88
-	 *
89
-	 * @since 9.0.0
90
-	 */
91
-	public function getAllTags($visibilityFilter = null, $nameSearchPattern = null);
81
+    /**
82
+     * Returns all known tags, optionally filtered by visibility.
83
+     *
84
+     * @param bool|null $visibilityFilter filter by visibility if non-null
85
+     * @param string $nameSearchPattern optional search pattern for the tag name
86
+     *
87
+     * @return \OCP\SystemTag\ISystemTag[] array of system tags or empty array if none found
88
+     *
89
+     * @since 9.0.0
90
+     */
91
+    public function getAllTags($visibilityFilter = null, $nameSearchPattern = null);
92 92
 
93
-	/**
94
-	 * Updates the given tag
95
-	 *
96
-	 * @param string $tagId tag id
97
-	 * @param string $newName the new tag name
98
-	 * @param bool $userVisible whether the tag is visible by users
99
-	 * @param bool $userAssignable whether the tag is assignable by users
100
-	 *
101
-	 * @throws \OCP\SystemTag\TagNotFoundException if tag with the given id does not exist
102
-	 * @throws \OCP\SystemTag\TagAlreadyExistsException if there is already another tag
103
-	 * with the same attributes
104
-	 *
105
-	 * @since 9.0.0
106
-	 */
107
-	public function updateTag($tagId, $newName, $userVisible, $userAssignable);
93
+    /**
94
+     * Updates the given tag
95
+     *
96
+     * @param string $tagId tag id
97
+     * @param string $newName the new tag name
98
+     * @param bool $userVisible whether the tag is visible by users
99
+     * @param bool $userAssignable whether the tag is assignable by users
100
+     *
101
+     * @throws \OCP\SystemTag\TagNotFoundException if tag with the given id does not exist
102
+     * @throws \OCP\SystemTag\TagAlreadyExistsException if there is already another tag
103
+     * with the same attributes
104
+     *
105
+     * @since 9.0.0
106
+     */
107
+    public function updateTag($tagId, $newName, $userVisible, $userAssignable);
108 108
 
109
-	/**
110
-	 * Delete the given tags from the database and all their relationships.
111
-	 *
112
-	 * @param string|array $tagIds array of tag ids
113
-	 *
114
-	 * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist
115
-	 *
116
-	 * @since 9.0.0
117
-	 */
118
-	public function deleteTags($tagIds);
109
+    /**
110
+     * Delete the given tags from the database and all their relationships.
111
+     *
112
+     * @param string|array $tagIds array of tag ids
113
+     *
114
+     * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist
115
+     *
116
+     * @since 9.0.0
117
+     */
118
+    public function deleteTags($tagIds);
119 119
 
120
-	/**
121
-	 * Checks whether the given user is allowed to assign/unassign the tag with the
122
-	 * given id.
123
-	 *
124
-	 * @param ISystemTag $tag tag to check permission for
125
-	 * @param IUser $user user to check permission for
126
-	 *
127
-	 * @return true if the user is allowed to assign/unassign the tag, false otherwise
128
-	 *
129
-	 * @since 9.1.0
130
-	 */
131
-	public function canUserAssignTag(ISystemTag $tag, IUser $user);
120
+    /**
121
+     * Checks whether the given user is allowed to assign/unassign the tag with the
122
+     * given id.
123
+     *
124
+     * @param ISystemTag $tag tag to check permission for
125
+     * @param IUser $user user to check permission for
126
+     *
127
+     * @return true if the user is allowed to assign/unassign the tag, false otherwise
128
+     *
129
+     * @since 9.1.0
130
+     */
131
+    public function canUserAssignTag(ISystemTag $tag, IUser $user);
132 132
 
133
-	/**
134
-	 * Checks whether the given user is allowed to see the tag with the given id.
135
-	 *
136
-	 * @param ISystemTag $tag tag to check permission for
137
-	 * @param IUser $user user to check permission for
138
-	 *
139
-	 * @return true if the user can see the tag, false otherwise
140
-	 *
141
-	 * @since 9.1.0
142
-	 */
143
-	public function canUserSeeTag(ISystemTag $tag, IUser $userId);
133
+    /**
134
+     * Checks whether the given user is allowed to see the tag with the given id.
135
+     *
136
+     * @param ISystemTag $tag tag to check permission for
137
+     * @param IUser $user user to check permission for
138
+     *
139
+     * @return true if the user can see the tag, false otherwise
140
+     *
141
+     * @since 9.1.0
142
+     */
143
+    public function canUserSeeTag(ISystemTag $tag, IUser $userId);
144 144
 
145
-	/**
146
-	 * Set groups that can assign a given tag.
147
-	 *
148
-	 * @param ISystemTag $tag tag for group assignment
149
-	 * @param string[] $groupIds group ids of groups that can assign/unassign the tag
150
-	 *
151
-	 * @since 9.1.0
152
-	 */
153
-	public function setTagGroups(ISystemTag $tag, $groupIds);
145
+    /**
146
+     * Set groups that can assign a given tag.
147
+     *
148
+     * @param ISystemTag $tag tag for group assignment
149
+     * @param string[] $groupIds group ids of groups that can assign/unassign the tag
150
+     *
151
+     * @since 9.1.0
152
+     */
153
+    public function setTagGroups(ISystemTag $tag, $groupIds);
154 154
 
155
-	/**
156
-	 * Get groups that can assign a given tag.
157
-	 *
158
-	 * @param ISystemTag $tag tag for group assignment
159
-	 *
160
-	 * @return string[] group ids of groups that can assign/unassign the tag
161
-	 *
162
-	 * @since 9.1.0
163
-	 */
164
-	public function getTagGroups(ISystemTag $tag);
155
+    /**
156
+     * Get groups that can assign a given tag.
157
+     *
158
+     * @param ISystemTag $tag tag for group assignment
159
+     *
160
+     * @return string[] group ids of groups that can assign/unassign the tag
161
+     *
162
+     * @since 9.1.0
163
+     */
164
+    public function getTagGroups(ISystemTag $tag);
165 165
 }
Please login to merge, or discard this patch.
lib/private/Files/View.php 3 patches
Doc Comments   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
 	 * and does not take the chroot into account )
201 201
 	 *
202 202
 	 * @param string $path
203
-	 * @return \OCP\Files\Mount\IMountPoint
203
+	 * @return Mount\MountPoint|null
204 204
 	 */
205 205
 	public function getMount($path) {
206 206
 		return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
@@ -963,7 +963,7 @@  discard block
 block discarded – undo
963 963
 
964 964
 	/**
965 965
 	 * @param string $path
966
-	 * @return bool|string
966
+	 * @return string|false
967 967
 	 * @throws \OCP\Files\InvalidPathException
968 968
 	 */
969 969
 	public function toTmpFile($path) {
@@ -1078,7 +1078,7 @@  discard block
 block discarded – undo
1078 1078
 	 * @param string $path
1079 1079
 	 * @param array $hooks (optional)
1080 1080
 	 * @param mixed $extraParam (optional)
1081
-	 * @return mixed
1081
+	 * @return string
1082 1082
 	 * @throws \Exception
1083 1083
 	 *
1084 1084
 	 * This method takes requests for basic filesystem functions (e.g. reading & writing
@@ -2086,7 +2086,7 @@  discard block
 block discarded – undo
2086 2086
 
2087 2087
 	/**
2088 2088
 	 * @param string $filename
2089
-	 * @return array
2089
+	 * @return string[]
2090 2090
 	 * @throws \OC\User\NoUserException
2091 2091
 	 * @throws NotFoundException
2092 2092
 	 */
Please login to merge, or discard this patch.
Indentation   +2083 added lines, -2083 removed lines patch added patch discarded remove patch
@@ -80,2087 +80,2087 @@
 block discarded – undo
80 80
  * \OC\Files\Storage\Storage object
81 81
  */
82 82
 class View {
83
-	/** @var string */
84
-	private $fakeRoot = '';
85
-
86
-	/**
87
-	 * @var \OCP\Lock\ILockingProvider
88
-	 */
89
-	protected $lockingProvider;
90
-
91
-	private $lockingEnabled;
92
-
93
-	private $updaterEnabled = true;
94
-
95
-	/** @var \OC\User\Manager */
96
-	private $userManager;
97
-
98
-	/** @var \OCP\ILogger */
99
-	private $logger;
100
-
101
-	/**
102
-	 * @param string $root
103
-	 * @throws \Exception If $root contains an invalid path
104
-	 */
105
-	public function __construct($root = '') {
106
-		if (is_null($root)) {
107
-			throw new \InvalidArgumentException('Root can\'t be null');
108
-		}
109
-		if (!Filesystem::isValidPath($root)) {
110
-			throw new \Exception();
111
-		}
112
-
113
-		$this->fakeRoot = $root;
114
-		$this->lockingProvider = \OC::$server->getLockingProvider();
115
-		$this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
116
-		$this->userManager = \OC::$server->getUserManager();
117
-		$this->logger = \OC::$server->getLogger();
118
-	}
119
-
120
-	public function getAbsolutePath($path = '/') {
121
-		if ($path === null) {
122
-			return null;
123
-		}
124
-		$this->assertPathLength($path);
125
-		if ($path === '') {
126
-			$path = '/';
127
-		}
128
-		if ($path[0] !== '/') {
129
-			$path = '/' . $path;
130
-		}
131
-		return $this->fakeRoot . $path;
132
-	}
133
-
134
-	/**
135
-	 * change the root to a fake root
136
-	 *
137
-	 * @param string $fakeRoot
138
-	 * @return boolean|null
139
-	 */
140
-	public function chroot($fakeRoot) {
141
-		if (!$fakeRoot == '') {
142
-			if ($fakeRoot[0] !== '/') {
143
-				$fakeRoot = '/' . $fakeRoot;
144
-			}
145
-		}
146
-		$this->fakeRoot = $fakeRoot;
147
-	}
148
-
149
-	/**
150
-	 * get the fake root
151
-	 *
152
-	 * @return string
153
-	 */
154
-	public function getRoot() {
155
-		return $this->fakeRoot;
156
-	}
157
-
158
-	/**
159
-	 * get path relative to the root of the view
160
-	 *
161
-	 * @param string $path
162
-	 * @return string
163
-	 */
164
-	public function getRelativePath($path) {
165
-		$this->assertPathLength($path);
166
-		if ($this->fakeRoot == '') {
167
-			return $path;
168
-		}
169
-
170
-		if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
171
-			return '/';
172
-		}
173
-
174
-		// missing slashes can cause wrong matches!
175
-		$root = rtrim($this->fakeRoot, '/') . '/';
176
-
177
-		if (strpos($path, $root) !== 0) {
178
-			return null;
179
-		} else {
180
-			$path = substr($path, strlen($this->fakeRoot));
181
-			if (strlen($path) === 0) {
182
-				return '/';
183
-			} else {
184
-				return $path;
185
-			}
186
-		}
187
-	}
188
-
189
-	/**
190
-	 * get the mountpoint of the storage object for a path
191
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
192
-	 * returned mountpoint is relative to the absolute root of the filesystem
193
-	 * and does not take the chroot into account )
194
-	 *
195
-	 * @param string $path
196
-	 * @return string
197
-	 */
198
-	public function getMountPoint($path) {
199
-		return Filesystem::getMountPoint($this->getAbsolutePath($path));
200
-	}
201
-
202
-	/**
203
-	 * get the mountpoint of the storage object for a path
204
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
205
-	 * returned mountpoint is relative to the absolute root of the filesystem
206
-	 * and does not take the chroot into account )
207
-	 *
208
-	 * @param string $path
209
-	 * @return \OCP\Files\Mount\IMountPoint
210
-	 */
211
-	public function getMount($path) {
212
-		return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
213
-	}
214
-
215
-	/**
216
-	 * resolve a path to a storage and internal path
217
-	 *
218
-	 * @param string $path
219
-	 * @return array an array consisting of the storage and the internal path
220
-	 */
221
-	public function resolvePath($path) {
222
-		$a = $this->getAbsolutePath($path);
223
-		$p = Filesystem::normalizePath($a);
224
-		return Filesystem::resolvePath($p);
225
-	}
226
-
227
-	/**
228
-	 * return the path to a local version of the file
229
-	 * we need this because we can't know if a file is stored local or not from
230
-	 * outside the filestorage and for some purposes a local file is needed
231
-	 *
232
-	 * @param string $path
233
-	 * @return string
234
-	 */
235
-	public function getLocalFile($path) {
236
-		$parent = substr($path, 0, strrpos($path, '/'));
237
-		$path = $this->getAbsolutePath($path);
238
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
239
-		if (Filesystem::isValidPath($parent) and $storage) {
240
-			return $storage->getLocalFile($internalPath);
241
-		} else {
242
-			return null;
243
-		}
244
-	}
245
-
246
-	/**
247
-	 * @param string $path
248
-	 * @return string
249
-	 */
250
-	public function getLocalFolder($path) {
251
-		$parent = substr($path, 0, strrpos($path, '/'));
252
-		$path = $this->getAbsolutePath($path);
253
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
254
-		if (Filesystem::isValidPath($parent) and $storage) {
255
-			return $storage->getLocalFolder($internalPath);
256
-		} else {
257
-			return null;
258
-		}
259
-	}
260
-
261
-	/**
262
-	 * the following functions operate with arguments and return values identical
263
-	 * to those of their PHP built-in equivalents. Mostly they are merely wrappers
264
-	 * for \OC\Files\Storage\Storage via basicOperation().
265
-	 */
266
-	public function mkdir($path) {
267
-		return $this->basicOperation('mkdir', $path, array('create', 'write'));
268
-	}
269
-
270
-	/**
271
-	 * remove mount point
272
-	 *
273
-	 * @param \OC\Files\Mount\MoveableMount $mount
274
-	 * @param string $path relative to data/
275
-	 * @return boolean
276
-	 */
277
-	protected function removeMount($mount, $path) {
278
-		if ($mount instanceof MoveableMount) {
279
-			// cut of /user/files to get the relative path to data/user/files
280
-			$pathParts = explode('/', $path, 4);
281
-			$relPath = '/' . $pathParts[3];
282
-			$this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283
-			\OC_Hook::emit(
284
-				Filesystem::CLASSNAME, "umount",
285
-				array(Filesystem::signal_param_path => $relPath)
286
-			);
287
-			$this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
288
-			$result = $mount->removeMount();
289
-			$this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
290
-			if ($result) {
291
-				\OC_Hook::emit(
292
-					Filesystem::CLASSNAME, "post_umount",
293
-					array(Filesystem::signal_param_path => $relPath)
294
-				);
295
-			}
296
-			$this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
297
-			return $result;
298
-		} else {
299
-			// do not allow deleting the storage's root / the mount point
300
-			// because for some storages it might delete the whole contents
301
-			// but isn't supposed to work that way
302
-			return false;
303
-		}
304
-	}
305
-
306
-	public function disableCacheUpdate() {
307
-		$this->updaterEnabled = false;
308
-	}
309
-
310
-	public function enableCacheUpdate() {
311
-		$this->updaterEnabled = true;
312
-	}
313
-
314
-	protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
315
-		if ($this->updaterEnabled) {
316
-			if (is_null($time)) {
317
-				$time = time();
318
-			}
319
-			$storage->getUpdater()->update($internalPath, $time);
320
-		}
321
-	}
322
-
323
-	protected function removeUpdate(Storage $storage, $internalPath) {
324
-		if ($this->updaterEnabled) {
325
-			$storage->getUpdater()->remove($internalPath);
326
-		}
327
-	}
328
-
329
-	protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
330
-		if ($this->updaterEnabled) {
331
-			$targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
332
-		}
333
-	}
334
-
335
-	/**
336
-	 * @param string $path
337
-	 * @return bool|mixed
338
-	 */
339
-	public function rmdir($path) {
340
-		$absolutePath = $this->getAbsolutePath($path);
341
-		$mount = Filesystem::getMountManager()->find($absolutePath);
342
-		if ($mount->getInternalPath($absolutePath) === '') {
343
-			return $this->removeMount($mount, $absolutePath);
344
-		}
345
-		if ($this->is_dir($path)) {
346
-			$result = $this->basicOperation('rmdir', $path, array('delete'));
347
-		} else {
348
-			$result = false;
349
-		}
350
-
351
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
352
-			$storage = $mount->getStorage();
353
-			$internalPath = $mount->getInternalPath($absolutePath);
354
-			$storage->getUpdater()->remove($internalPath);
355
-		}
356
-		return $result;
357
-	}
358
-
359
-	/**
360
-	 * @param string $path
361
-	 * @return resource
362
-	 */
363
-	public function opendir($path) {
364
-		return $this->basicOperation('opendir', $path, array('read'));
365
-	}
366
-
367
-	/**
368
-	 * @param string $path
369
-	 * @return bool|mixed
370
-	 */
371
-	public function is_dir($path) {
372
-		if ($path == '/') {
373
-			return true;
374
-		}
375
-		return $this->basicOperation('is_dir', $path);
376
-	}
377
-
378
-	/**
379
-	 * @param string $path
380
-	 * @return bool|mixed
381
-	 */
382
-	public function is_file($path) {
383
-		if ($path == '/') {
384
-			return false;
385
-		}
386
-		return $this->basicOperation('is_file', $path);
387
-	}
388
-
389
-	/**
390
-	 * @param string $path
391
-	 * @return mixed
392
-	 */
393
-	public function stat($path) {
394
-		return $this->basicOperation('stat', $path);
395
-	}
396
-
397
-	/**
398
-	 * @param string $path
399
-	 * @return mixed
400
-	 */
401
-	public function filetype($path) {
402
-		return $this->basicOperation('filetype', $path);
403
-	}
404
-
405
-	/**
406
-	 * @param string $path
407
-	 * @return mixed
408
-	 */
409
-	public function filesize($path) {
410
-		return $this->basicOperation('filesize', $path);
411
-	}
412
-
413
-	/**
414
-	 * @param string $path
415
-	 * @return bool|mixed
416
-	 * @throws \OCP\Files\InvalidPathException
417
-	 */
418
-	public function readfile($path) {
419
-		$this->assertPathLength($path);
420
-		@ob_end_clean();
421
-		$handle = $this->fopen($path, 'rb');
422
-		if ($handle) {
423
-			$chunkSize = 8192; // 8 kB chunks
424
-			while (!feof($handle)) {
425
-				echo fread($handle, $chunkSize);
426
-				flush();
427
-			}
428
-			fclose($handle);
429
-			$size = $this->filesize($path);
430
-			return $size;
431
-		}
432
-		return false;
433
-	}
434
-
435
-	/**
436
-	 * @param string $path
437
-	 * @param int $from
438
-	 * @param int $to
439
-	 * @return bool|mixed
440
-	 * @throws \OCP\Files\InvalidPathException
441
-	 * @throws \OCP\Files\UnseekableException
442
-	 */
443
-	public function readfilePart($path, $from, $to) {
444
-		$this->assertPathLength($path);
445
-		@ob_end_clean();
446
-		$handle = $this->fopen($path, 'rb');
447
-		if ($handle) {
448
-			$chunkSize = 8192; // 8 kB chunks
449
-			$startReading = true;
450
-
451
-			if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
452
-				// forward file handle via chunked fread because fseek seem to have failed
453
-
454
-				$end = $from + 1;
455
-				while (!feof($handle) && ftell($handle) < $end) {
456
-					$len = $from - ftell($handle);
457
-					if ($len > $chunkSize) {
458
-						$len = $chunkSize;
459
-					}
460
-					$result = fread($handle, $len);
461
-
462
-					if ($result === false) {
463
-						$startReading = false;
464
-						break;
465
-					}
466
-				}
467
-			}
468
-
469
-			if ($startReading) {
470
-				$end = $to + 1;
471
-				while (!feof($handle) && ftell($handle) < $end) {
472
-					$len = $end - ftell($handle);
473
-					if ($len > $chunkSize) {
474
-						$len = $chunkSize;
475
-					}
476
-					echo fread($handle, $len);
477
-					flush();
478
-				}
479
-				$size = ftell($handle) - $from;
480
-				return $size;
481
-			}
482
-
483
-			throw new \OCP\Files\UnseekableException('fseek error');
484
-		}
485
-		return false;
486
-	}
487
-
488
-	/**
489
-	 * @param string $path
490
-	 * @return mixed
491
-	 */
492
-	public function isCreatable($path) {
493
-		return $this->basicOperation('isCreatable', $path);
494
-	}
495
-
496
-	/**
497
-	 * @param string $path
498
-	 * @return mixed
499
-	 */
500
-	public function isReadable($path) {
501
-		return $this->basicOperation('isReadable', $path);
502
-	}
503
-
504
-	/**
505
-	 * @param string $path
506
-	 * @return mixed
507
-	 */
508
-	public function isUpdatable($path) {
509
-		return $this->basicOperation('isUpdatable', $path);
510
-	}
511
-
512
-	/**
513
-	 * @param string $path
514
-	 * @return bool|mixed
515
-	 */
516
-	public function isDeletable($path) {
517
-		$absolutePath = $this->getAbsolutePath($path);
518
-		$mount = Filesystem::getMountManager()->find($absolutePath);
519
-		if ($mount->getInternalPath($absolutePath) === '') {
520
-			return $mount instanceof MoveableMount;
521
-		}
522
-		return $this->basicOperation('isDeletable', $path);
523
-	}
524
-
525
-	/**
526
-	 * @param string $path
527
-	 * @return mixed
528
-	 */
529
-	public function isSharable($path) {
530
-		return $this->basicOperation('isSharable', $path);
531
-	}
532
-
533
-	/**
534
-	 * @param string $path
535
-	 * @return bool|mixed
536
-	 */
537
-	public function file_exists($path) {
538
-		if ($path == '/') {
539
-			return true;
540
-		}
541
-		return $this->basicOperation('file_exists', $path);
542
-	}
543
-
544
-	/**
545
-	 * @param string $path
546
-	 * @return mixed
547
-	 */
548
-	public function filemtime($path) {
549
-		return $this->basicOperation('filemtime', $path);
550
-	}
551
-
552
-	/**
553
-	 * @param string $path
554
-	 * @param int|string $mtime
555
-	 * @return bool
556
-	 */
557
-	public function touch($path, $mtime = null) {
558
-		if (!is_null($mtime) and !is_numeric($mtime)) {
559
-			$mtime = strtotime($mtime);
560
-		}
561
-
562
-		$hooks = array('touch');
563
-
564
-		if (!$this->file_exists($path)) {
565
-			$hooks[] = 'create';
566
-			$hooks[] = 'write';
567
-		}
568
-		$result = $this->basicOperation('touch', $path, $hooks, $mtime);
569
-		if (!$result) {
570
-			// If create file fails because of permissions on external storage like SMB folders,
571
-			// check file exists and return false if not.
572
-			if (!$this->file_exists($path)) {
573
-				return false;
574
-			}
575
-			if (is_null($mtime)) {
576
-				$mtime = time();
577
-			}
578
-			//if native touch fails, we emulate it by changing the mtime in the cache
579
-			$this->putFileInfo($path, array('mtime' => floor($mtime)));
580
-		}
581
-		return true;
582
-	}
583
-
584
-	/**
585
-	 * @param string $path
586
-	 * @return mixed
587
-	 */
588
-	public function file_get_contents($path) {
589
-		return $this->basicOperation('file_get_contents', $path, array('read'));
590
-	}
591
-
592
-	/**
593
-	 * @param bool $exists
594
-	 * @param string $path
595
-	 * @param bool $run
596
-	 */
597
-	protected function emit_file_hooks_pre($exists, $path, &$run) {
598
-		if (!$exists) {
599
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
600
-				Filesystem::signal_param_path => $this->getHookPath($path),
601
-				Filesystem::signal_param_run => &$run,
602
-			));
603
-		} else {
604
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
605
-				Filesystem::signal_param_path => $this->getHookPath($path),
606
-				Filesystem::signal_param_run => &$run,
607
-			));
608
-		}
609
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
610
-			Filesystem::signal_param_path => $this->getHookPath($path),
611
-			Filesystem::signal_param_run => &$run,
612
-		));
613
-	}
614
-
615
-	/**
616
-	 * @param bool $exists
617
-	 * @param string $path
618
-	 */
619
-	protected function emit_file_hooks_post($exists, $path) {
620
-		if (!$exists) {
621
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
622
-				Filesystem::signal_param_path => $this->getHookPath($path),
623
-			));
624
-		} else {
625
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
626
-				Filesystem::signal_param_path => $this->getHookPath($path),
627
-			));
628
-		}
629
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
630
-			Filesystem::signal_param_path => $this->getHookPath($path),
631
-		));
632
-	}
633
-
634
-	/**
635
-	 * @param string $path
636
-	 * @param mixed $data
637
-	 * @return bool|mixed
638
-	 * @throws \Exception
639
-	 */
640
-	public function file_put_contents($path, $data) {
641
-		if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
642
-			$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
643
-			if (Filesystem::isValidPath($path)
644
-				and !Filesystem::isFileBlacklisted($path)
645
-			) {
646
-				$path = $this->getRelativePath($absolutePath);
647
-
648
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
649
-
650
-				$exists = $this->file_exists($path);
651
-				$run = true;
652
-				if ($this->shouldEmitHooks($path)) {
653
-					$this->emit_file_hooks_pre($exists, $path, $run);
654
-				}
655
-				if (!$run) {
656
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
657
-					return false;
658
-				}
659
-
660
-				$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
661
-
662
-				/** @var \OC\Files\Storage\Storage $storage */
663
-				list($storage, $internalPath) = $this->resolvePath($path);
664
-				$target = $storage->fopen($internalPath, 'w');
665
-				if ($target) {
666
-					list (, $result) = \OC_Helper::streamCopy($data, $target);
667
-					fclose($target);
668
-					fclose($data);
669
-
670
-					$this->writeUpdate($storage, $internalPath);
671
-
672
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
673
-
674
-					if ($this->shouldEmitHooks($path) && $result !== false) {
675
-						$this->emit_file_hooks_post($exists, $path);
676
-					}
677
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
678
-					return $result;
679
-				} else {
680
-					$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
681
-					return false;
682
-				}
683
-			} else {
684
-				return false;
685
-			}
686
-		} else {
687
-			$hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
688
-			return $this->basicOperation('file_put_contents', $path, $hooks, $data);
689
-		}
690
-	}
691
-
692
-	/**
693
-	 * @param string $path
694
-	 * @return bool|mixed
695
-	 */
696
-	public function unlink($path) {
697
-		if ($path === '' || $path === '/') {
698
-			// do not allow deleting the root
699
-			return false;
700
-		}
701
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
702
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
704
-		if ($mount and $mount->getInternalPath($absolutePath) === '') {
705
-			return $this->removeMount($mount, $absolutePath);
706
-		}
707
-		if ($this->is_dir($path)) {
708
-			$result = $this->basicOperation('rmdir', $path, ['delete']);
709
-		} else {
710
-			$result = $this->basicOperation('unlink', $path, ['delete']);
711
-		}
712
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
713
-			$storage = $mount->getStorage();
714
-			$internalPath = $mount->getInternalPath($absolutePath);
715
-			$storage->getUpdater()->remove($internalPath);
716
-			return true;
717
-		} else {
718
-			return $result;
719
-		}
720
-	}
721
-
722
-	/**
723
-	 * @param string $directory
724
-	 * @return bool|mixed
725
-	 */
726
-	public function deleteAll($directory) {
727
-		return $this->rmdir($directory);
728
-	}
729
-
730
-	/**
731
-	 * Rename/move a file or folder from the source path to target path.
732
-	 *
733
-	 * @param string $path1 source path
734
-	 * @param string $path2 target path
735
-	 *
736
-	 * @return bool|mixed
737
-	 */
738
-	public function rename($path1, $path2) {
739
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
740
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
741
-		$result = false;
742
-		if (
743
-			Filesystem::isValidPath($path2)
744
-			and Filesystem::isValidPath($path1)
745
-			and !Filesystem::isFileBlacklisted($path2)
746
-		) {
747
-			$path1 = $this->getRelativePath($absolutePath1);
748
-			$path2 = $this->getRelativePath($absolutePath2);
749
-			$exists = $this->file_exists($path2);
750
-
751
-			if ($path1 == null or $path2 == null) {
752
-				return false;
753
-			}
754
-
755
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
756
-			try {
757
-				$this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
758
-
759
-				$run = true;
760
-				if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
761
-					// if it was a rename from a part file to a regular file it was a write and not a rename operation
762
-					$this->emit_file_hooks_pre($exists, $path2, $run);
763
-				} elseif ($this->shouldEmitHooks($path1)) {
764
-					\OC_Hook::emit(
765
-						Filesystem::CLASSNAME, Filesystem::signal_rename,
766
-						array(
767
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
768
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
769
-							Filesystem::signal_param_run => &$run
770
-						)
771
-					);
772
-				}
773
-				if ($run) {
774
-					$this->verifyPath(dirname($path2), basename($path2));
775
-
776
-					$manager = Filesystem::getMountManager();
777
-					$mount1 = $this->getMount($path1);
778
-					$mount2 = $this->getMount($path2);
779
-					$storage1 = $mount1->getStorage();
780
-					$storage2 = $mount2->getStorage();
781
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
782
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
783
-
784
-					$this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
785
-					try {
786
-						$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
787
-
788
-						if ($internalPath1 === '') {
789
-							if ($mount1 instanceof MoveableMount) {
790
-								if ($this->isTargetAllowed($absolutePath2)) {
791
-									/**
792
-									 * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
793
-									 */
794
-									$sourceMountPoint = $mount1->getMountPoint();
795
-									$result = $mount1->moveMount($absolutePath2);
796
-									$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
797
-								} else {
798
-									$result = false;
799
-								}
800
-							} else {
801
-								$result = false;
802
-							}
803
-							// moving a file/folder within the same mount point
804
-						} elseif ($storage1 === $storage2) {
805
-							if ($storage1) {
806
-								$result = $storage1->rename($internalPath1, $internalPath2);
807
-							} else {
808
-								$result = false;
809
-							}
810
-							// moving a file/folder between storages (from $storage1 to $storage2)
811
-						} else {
812
-							$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
813
-						}
814
-
815
-						if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
816
-							// if it was a rename from a part file to a regular file it was a write and not a rename operation
817
-							$this->writeUpdate($storage2, $internalPath2);
818
-						} else if ($result) {
819
-							if ($internalPath1 !== '') { // don't do a cache update for moved mounts
820
-								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821
-							}
822
-						}
823
-					} catch(\Exception $e) {
824
-						throw $e;
825
-					} finally {
826
-						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
827
-						$this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
828
-					}
829
-
830
-					if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
831
-						if ($this->shouldEmitHooks()) {
832
-							$this->emit_file_hooks_post($exists, $path2);
833
-						}
834
-					} elseif ($result) {
835
-						if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
836
-							\OC_Hook::emit(
837
-								Filesystem::CLASSNAME,
838
-								Filesystem::signal_post_rename,
839
-								array(
840
-									Filesystem::signal_param_oldpath => $this->getHookPath($path1),
841
-									Filesystem::signal_param_newpath => $this->getHookPath($path2)
842
-								)
843
-							);
844
-						}
845
-					}
846
-				}
847
-			} catch(\Exception $e) {
848
-				throw $e;
849
-			} finally {
850
-				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
851
-				$this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
852
-			}
853
-		}
854
-		return $result;
855
-	}
856
-
857
-	/**
858
-	 * Copy a file/folder from the source path to target path
859
-	 *
860
-	 * @param string $path1 source path
861
-	 * @param string $path2 target path
862
-	 * @param bool $preserveMtime whether to preserve mtime on the copy
863
-	 *
864
-	 * @return bool|mixed
865
-	 */
866
-	public function copy($path1, $path2, $preserveMtime = false) {
867
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
868
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
869
-		$result = false;
870
-		if (
871
-			Filesystem::isValidPath($path2)
872
-			and Filesystem::isValidPath($path1)
873
-			and !Filesystem::isFileBlacklisted($path2)
874
-		) {
875
-			$path1 = $this->getRelativePath($absolutePath1);
876
-			$path2 = $this->getRelativePath($absolutePath2);
877
-
878
-			if ($path1 == null or $path2 == null) {
879
-				return false;
880
-			}
881
-			$run = true;
882
-
883
-			$this->lockFile($path2, ILockingProvider::LOCK_SHARED);
884
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED);
885
-			$lockTypePath1 = ILockingProvider::LOCK_SHARED;
886
-			$lockTypePath2 = ILockingProvider::LOCK_SHARED;
887
-
888
-			try {
889
-
890
-				$exists = $this->file_exists($path2);
891
-				if ($this->shouldEmitHooks()) {
892
-					\OC_Hook::emit(
893
-						Filesystem::CLASSNAME,
894
-						Filesystem::signal_copy,
895
-						array(
896
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
897
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
898
-							Filesystem::signal_param_run => &$run
899
-						)
900
-					);
901
-					$this->emit_file_hooks_pre($exists, $path2, $run);
902
-				}
903
-				if ($run) {
904
-					$mount1 = $this->getMount($path1);
905
-					$mount2 = $this->getMount($path2);
906
-					$storage1 = $mount1->getStorage();
907
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
908
-					$storage2 = $mount2->getStorage();
909
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
910
-
911
-					$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
912
-					$lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
913
-
914
-					if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
915
-						if ($storage1) {
916
-							$result = $storage1->copy($internalPath1, $internalPath2);
917
-						} else {
918
-							$result = false;
919
-						}
920
-					} else {
921
-						$result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
922
-					}
923
-
924
-					$this->writeUpdate($storage2, $internalPath2);
925
-
926
-					$this->changeLock($path2, ILockingProvider::LOCK_SHARED);
927
-					$lockTypePath2 = ILockingProvider::LOCK_SHARED;
928
-
929
-					if ($this->shouldEmitHooks() && $result !== false) {
930
-						\OC_Hook::emit(
931
-							Filesystem::CLASSNAME,
932
-							Filesystem::signal_post_copy,
933
-							array(
934
-								Filesystem::signal_param_oldpath => $this->getHookPath($path1),
935
-								Filesystem::signal_param_newpath => $this->getHookPath($path2)
936
-							)
937
-						);
938
-						$this->emit_file_hooks_post($exists, $path2);
939
-					}
940
-
941
-				}
942
-			} catch (\Exception $e) {
943
-				$this->unlockFile($path2, $lockTypePath2);
944
-				$this->unlockFile($path1, $lockTypePath1);
945
-				throw $e;
946
-			}
947
-
948
-			$this->unlockFile($path2, $lockTypePath2);
949
-			$this->unlockFile($path1, $lockTypePath1);
950
-
951
-		}
952
-		return $result;
953
-	}
954
-
955
-	/**
956
-	 * @param string $path
957
-	 * @param string $mode 'r' or 'w'
958
-	 * @return resource
959
-	 */
960
-	public function fopen($path, $mode) {
961
-		$mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
962
-		$hooks = array();
963
-		switch ($mode) {
964
-			case 'r':
965
-				$hooks[] = 'read';
966
-				break;
967
-			case 'r+':
968
-			case 'w+':
969
-			case 'x+':
970
-			case 'a+':
971
-				$hooks[] = 'read';
972
-				$hooks[] = 'write';
973
-				break;
974
-			case 'w':
975
-			case 'x':
976
-			case 'a':
977
-				$hooks[] = 'write';
978
-				break;
979
-			default:
980
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
981
-		}
982
-
983
-		if ($mode !== 'r' && $mode !== 'w') {
984
-			\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');
985
-		}
986
-
987
-		return $this->basicOperation('fopen', $path, $hooks, $mode);
988
-	}
989
-
990
-	/**
991
-	 * @param string $path
992
-	 * @return bool|string
993
-	 * @throws \OCP\Files\InvalidPathException
994
-	 */
995
-	public function toTmpFile($path) {
996
-		$this->assertPathLength($path);
997
-		if (Filesystem::isValidPath($path)) {
998
-			$source = $this->fopen($path, 'r');
999
-			if ($source) {
1000
-				$extension = pathinfo($path, PATHINFO_EXTENSION);
1001
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1002
-				file_put_contents($tmpFile, $source);
1003
-				return $tmpFile;
1004
-			} else {
1005
-				return false;
1006
-			}
1007
-		} else {
1008
-			return false;
1009
-		}
1010
-	}
1011
-
1012
-	/**
1013
-	 * @param string $tmpFile
1014
-	 * @param string $path
1015
-	 * @return bool|mixed
1016
-	 * @throws \OCP\Files\InvalidPathException
1017
-	 */
1018
-	public function fromTmpFile($tmpFile, $path) {
1019
-		$this->assertPathLength($path);
1020
-		if (Filesystem::isValidPath($path)) {
1021
-
1022
-			// Get directory that the file is going into
1023
-			$filePath = dirname($path);
1024
-
1025
-			// Create the directories if any
1026
-			if (!$this->file_exists($filePath)) {
1027
-				$result = $this->createParentDirectories($filePath);
1028
-				if ($result === false) {
1029
-					return false;
1030
-				}
1031
-			}
1032
-
1033
-			$source = fopen($tmpFile, 'r');
1034
-			if ($source) {
1035
-				$result = $this->file_put_contents($path, $source);
1036
-				// $this->file_put_contents() might have already closed
1037
-				// the resource, so we check it, before trying to close it
1038
-				// to avoid messages in the error log.
1039
-				if (is_resource($source)) {
1040
-					fclose($source);
1041
-				}
1042
-				unlink($tmpFile);
1043
-				return $result;
1044
-			} else {
1045
-				return false;
1046
-			}
1047
-		} else {
1048
-			return false;
1049
-		}
1050
-	}
1051
-
1052
-
1053
-	/**
1054
-	 * @param string $path
1055
-	 * @return mixed
1056
-	 * @throws \OCP\Files\InvalidPathException
1057
-	 */
1058
-	public function getMimeType($path) {
1059
-		$this->assertPathLength($path);
1060
-		return $this->basicOperation('getMimeType', $path);
1061
-	}
1062
-
1063
-	/**
1064
-	 * @param string $type
1065
-	 * @param string $path
1066
-	 * @param bool $raw
1067
-	 * @return bool|null|string
1068
-	 */
1069
-	public function hash($type, $path, $raw = false) {
1070
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1071
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1072
-		if (Filesystem::isValidPath($path)) {
1073
-			$path = $this->getRelativePath($absolutePath);
1074
-			if ($path == null) {
1075
-				return false;
1076
-			}
1077
-			if ($this->shouldEmitHooks($path)) {
1078
-				\OC_Hook::emit(
1079
-					Filesystem::CLASSNAME,
1080
-					Filesystem::signal_read,
1081
-					array(Filesystem::signal_param_path => $this->getHookPath($path))
1082
-				);
1083
-			}
1084
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1085
-			if ($storage) {
1086
-				$result = $storage->hash($type, $internalPath, $raw);
1087
-				return $result;
1088
-			}
1089
-		}
1090
-		return null;
1091
-	}
1092
-
1093
-	/**
1094
-	 * @param string $path
1095
-	 * @return mixed
1096
-	 * @throws \OCP\Files\InvalidPathException
1097
-	 */
1098
-	public function free_space($path = '/') {
1099
-		$this->assertPathLength($path);
1100
-		$result = $this->basicOperation('free_space', $path);
1101
-		if ($result === null) {
1102
-			throw new InvalidPathException();
1103
-		}
1104
-		return $result;
1105
-	}
1106
-
1107
-	/**
1108
-	 * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1109
-	 *
1110
-	 * @param string $operation
1111
-	 * @param string $path
1112
-	 * @param array $hooks (optional)
1113
-	 * @param mixed $extraParam (optional)
1114
-	 * @return mixed
1115
-	 * @throws \Exception
1116
-	 *
1117
-	 * This method takes requests for basic filesystem functions (e.g. reading & writing
1118
-	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1119
-	 * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1120
-	 */
1121
-	private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1122
-		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1123
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1124
-		if (Filesystem::isValidPath($path)
1125
-			and !Filesystem::isFileBlacklisted($path)
1126
-		) {
1127
-			$path = $this->getRelativePath($absolutePath);
1128
-			if ($path == null) {
1129
-				return false;
1130
-			}
1131
-
1132
-			if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1133
-				// always a shared lock during pre-hooks so the hook can read the file
1134
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
1135
-			}
1136
-
1137
-			$run = $this->runHooks($hooks, $path);
1138
-			/** @var \OC\Files\Storage\Storage $storage */
1139
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1140
-			if ($run and $storage) {
1141
-				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142
-					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1143
-				}
1144
-				try {
1145
-					if (!is_null($extraParam)) {
1146
-						$result = $storage->$operation($internalPath, $extraParam);
1147
-					} else {
1148
-						$result = $storage->$operation($internalPath);
1149
-					}
1150
-				} catch (\Exception $e) {
1151
-					if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1152
-						$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1153
-					} else if (in_array('read', $hooks)) {
1154
-						$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1155
-					}
1156
-					throw $e;
1157
-				}
1158
-
1159
-				if ($result && in_array('delete', $hooks) and $result) {
1160
-					$this->removeUpdate($storage, $internalPath);
1161
-				}
1162
-				if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1163
-					$this->writeUpdate($storage, $internalPath);
1164
-				}
1165
-				if ($result && in_array('touch', $hooks)) {
1166
-					$this->writeUpdate($storage, $internalPath, $extraParam);
1167
-				}
1168
-
1169
-				if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1170
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
1171
-				}
1172
-
1173
-				$unlockLater = false;
1174
-				if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1175
-					$unlockLater = true;
1176
-					// make sure our unlocking callback will still be called if connection is aborted
1177
-					ignore_user_abort(true);
1178
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1179
-						if (in_array('write', $hooks)) {
1180
-							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181
-						} else if (in_array('read', $hooks)) {
1182
-							$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1183
-						}
1184
-					});
1185
-				}
1186
-
1187
-				if ($this->shouldEmitHooks($path) && $result !== false) {
1188
-					if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1189
-						$this->runHooks($hooks, $path, true);
1190
-					}
1191
-				}
1192
-
1193
-				if (!$unlockLater
1194
-					&& (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1195
-				) {
1196
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1197
-				}
1198
-				return $result;
1199
-			} else {
1200
-				$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1201
-			}
1202
-		}
1203
-		return null;
1204
-	}
1205
-
1206
-	/**
1207
-	 * get the path relative to the default root for hook usage
1208
-	 *
1209
-	 * @param string $path
1210
-	 * @return string
1211
-	 */
1212
-	private function getHookPath($path) {
1213
-		if (!Filesystem::getView()) {
1214
-			return $path;
1215
-		}
1216
-		return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1217
-	}
1218
-
1219
-	private function shouldEmitHooks($path = '') {
1220
-		if ($path && Cache\Scanner::isPartialFile($path)) {
1221
-			return false;
1222
-		}
1223
-		if (!Filesystem::$loaded) {
1224
-			return false;
1225
-		}
1226
-		$defaultRoot = Filesystem::getRoot();
1227
-		if ($defaultRoot === null) {
1228
-			return false;
1229
-		}
1230
-		if ($this->fakeRoot === $defaultRoot) {
1231
-			return true;
1232
-		}
1233
-		$fullPath = $this->getAbsolutePath($path);
1234
-
1235
-		if ($fullPath === $defaultRoot) {
1236
-			return true;
1237
-		}
1238
-
1239
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1240
-	}
1241
-
1242
-	/**
1243
-	 * @param string[] $hooks
1244
-	 * @param string $path
1245
-	 * @param bool $post
1246
-	 * @return bool
1247
-	 */
1248
-	private function runHooks($hooks, $path, $post = false) {
1249
-		$relativePath = $path;
1250
-		$path = $this->getHookPath($path);
1251
-		$prefix = ($post) ? 'post_' : '';
1252
-		$run = true;
1253
-		if ($this->shouldEmitHooks($relativePath)) {
1254
-			foreach ($hooks as $hook) {
1255
-				if ($hook != 'read') {
1256
-					\OC_Hook::emit(
1257
-						Filesystem::CLASSNAME,
1258
-						$prefix . $hook,
1259
-						array(
1260
-							Filesystem::signal_param_run => &$run,
1261
-							Filesystem::signal_param_path => $path
1262
-						)
1263
-					);
1264
-				} elseif (!$post) {
1265
-					\OC_Hook::emit(
1266
-						Filesystem::CLASSNAME,
1267
-						$prefix . $hook,
1268
-						array(
1269
-							Filesystem::signal_param_path => $path
1270
-						)
1271
-					);
1272
-				}
1273
-			}
1274
-		}
1275
-		return $run;
1276
-	}
1277
-
1278
-	/**
1279
-	 * check if a file or folder has been updated since $time
1280
-	 *
1281
-	 * @param string $path
1282
-	 * @param int $time
1283
-	 * @return bool
1284
-	 */
1285
-	public function hasUpdated($path, $time) {
1286
-		return $this->basicOperation('hasUpdated', $path, array(), $time);
1287
-	}
1288
-
1289
-	/**
1290
-	 * @param string $ownerId
1291
-	 * @return \OC\User\User
1292
-	 */
1293
-	private function getUserObjectForOwner($ownerId) {
1294
-		$owner = $this->userManager->get($ownerId);
1295
-		if ($owner instanceof IUser) {
1296
-			return $owner;
1297
-		} else {
1298
-			return new User($ownerId, null);
1299
-		}
1300
-	}
1301
-
1302
-	/**
1303
-	 * Get file info from cache
1304
-	 *
1305
-	 * If the file is not in cached it will be scanned
1306
-	 * If the file has changed on storage the cache will be updated
1307
-	 *
1308
-	 * @param \OC\Files\Storage\Storage $storage
1309
-	 * @param string $internalPath
1310
-	 * @param string $relativePath
1311
-	 * @return ICacheEntry|bool
1312
-	 */
1313
-	private function getCacheEntry($storage, $internalPath, $relativePath) {
1314
-		$cache = $storage->getCache($internalPath);
1315
-		$data = $cache->get($internalPath);
1316
-		$watcher = $storage->getWatcher($internalPath);
1317
-
1318
-		try {
1319
-			// if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1320
-			if (!$data || $data['size'] === -1) {
1321
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1322
-				if (!$storage->file_exists($internalPath)) {
1323
-					$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1324
-					return false;
1325
-				}
1326
-				$scanner = $storage->getScanner($internalPath);
1327
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1328
-				$data = $cache->get($internalPath);
1329
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1330
-			} else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1331
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1332
-				$watcher->update($internalPath, $data);
1333
-				$storage->getPropagator()->propagateChange($internalPath, time());
1334
-				$data = $cache->get($internalPath);
1335
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1336
-			}
1337
-		} catch (LockedException $e) {
1338
-			// if the file is locked we just use the old cache info
1339
-		}
1340
-
1341
-		return $data;
1342
-	}
1343
-
1344
-	/**
1345
-	 * get the filesystem info
1346
-	 *
1347
-	 * @param string $path
1348
-	 * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1349
-	 * 'ext' to add only ext storage mount point sizes. Defaults to true.
1350
-	 * defaults to true
1351
-	 * @return \OC\Files\FileInfo|false False if file does not exist
1352
-	 */
1353
-	public function getFileInfo($path, $includeMountPoints = true) {
1354
-		$this->assertPathLength($path);
1355
-		if (!Filesystem::isValidPath($path)) {
1356
-			return false;
1357
-		}
1358
-		if (Cache\Scanner::isPartialFile($path)) {
1359
-			return $this->getPartFileInfo($path);
1360
-		}
1361
-		$relativePath = $path;
1362
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1363
-
1364
-		$mount = Filesystem::getMountManager()->find($path);
1365
-		if (!$mount) {
1366
-			return false;
1367
-		}
1368
-		$storage = $mount->getStorage();
1369
-		$internalPath = $mount->getInternalPath($path);
1370
-		if ($storage) {
1371
-			$data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1372
-
1373
-			if (!$data instanceof ICacheEntry) {
1374
-				return false;
1375
-			}
1376
-
1377
-			if ($mount instanceof MoveableMount && $internalPath === '') {
1378
-				$data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1379
-			}
1380
-
1381
-			$owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1382
-			$info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1383
-
1384
-			if ($data and isset($data['fileid'])) {
1385
-				if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1386
-					//add the sizes of other mount points to the folder
1387
-					$extOnly = ($includeMountPoints === 'ext');
1388
-					$mounts = Filesystem::getMountManager()->findIn($path);
1389
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1390
-						$subStorage = $mount->getStorage();
1391
-						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392
-					}));
1393
-				}
1394
-			}
1395
-
1396
-			return $info;
1397
-		}
1398
-
1399
-		return false;
1400
-	}
1401
-
1402
-	/**
1403
-	 * get the content of a directory
1404
-	 *
1405
-	 * @param string $directory path under datadirectory
1406
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1407
-	 * @return FileInfo[]
1408
-	 */
1409
-	public function getDirectoryContent($directory, $mimetype_filter = '') {
1410
-		$this->assertPathLength($directory);
1411
-		if (!Filesystem::isValidPath($directory)) {
1412
-			return [];
1413
-		}
1414
-		$path = $this->getAbsolutePath($directory);
1415
-		$path = Filesystem::normalizePath($path);
1416
-		$mount = $this->getMount($directory);
1417
-		if (!$mount) {
1418
-			return [];
1419
-		}
1420
-		$storage = $mount->getStorage();
1421
-		$internalPath = $mount->getInternalPath($path);
1422
-		if ($storage) {
1423
-			$cache = $storage->getCache($internalPath);
1424
-			$user = \OC_User::getUser();
1425
-
1426
-			$data = $this->getCacheEntry($storage, $internalPath, $directory);
1427
-
1428
-			if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1429
-				return [];
1430
-			}
1431
-
1432
-			$folderId = $data['fileid'];
1433
-			$contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1434
-
1435
-			$sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1436
-			/**
1437
-			 * @var \OC\Files\FileInfo[] $files
1438
-			 */
1439
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440
-				if ($sharingDisabled) {
1441
-					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442
-				}
1443
-				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1445
-			}, $contents);
1446
-
1447
-			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1448
-			$mounts = Filesystem::getMountManager()->findIn($path);
1449
-			$dirLength = strlen($path);
1450
-			foreach ($mounts as $mount) {
1451
-				$mountPoint = $mount->getMountPoint();
1452
-				$subStorage = $mount->getStorage();
1453
-				if ($subStorage) {
1454
-					$subCache = $subStorage->getCache('');
1455
-
1456
-					$rootEntry = $subCache->get('');
1457
-					if (!$rootEntry) {
1458
-						$subScanner = $subStorage->getScanner('');
1459
-						try {
1460
-							$subScanner->scanFile('');
1461
-						} catch (\OCP\Files\StorageNotAvailableException $e) {
1462
-							continue;
1463
-						} catch (\OCP\Files\StorageInvalidException $e) {
1464
-							continue;
1465
-						} catch (\Exception $e) {
1466
-							// sometimes when the storage is not available it can be any exception
1467
-							\OC::$server->getLogger()->logException($e, [
1468
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1469
-								'level' => \OCP\Util::ERROR,
1470
-								'app' => 'lib',
1471
-							]);
1472
-							continue;
1473
-						}
1474
-						$rootEntry = $subCache->get('');
1475
-					}
1476
-
1477
-					if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1478
-						$relativePath = trim(substr($mountPoint, $dirLength), '/');
1479
-						if ($pos = strpos($relativePath, '/')) {
1480
-							//mountpoint inside subfolder add size to the correct folder
1481
-							$entryName = substr($relativePath, 0, $pos);
1482
-							foreach ($files as &$entry) {
1483
-								if ($entry->getName() === $entryName) {
1484
-									$entry->addSubEntry($rootEntry, $mountPoint);
1485
-								}
1486
-							}
1487
-						} else { //mountpoint in this folder, add an entry for it
1488
-							$rootEntry['name'] = $relativePath;
1489
-							$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1490
-							$permissions = $rootEntry['permissions'];
1491
-							// do not allow renaming/deleting the mount point if they are not shared files/folders
1492
-							// for shared files/folders we use the permissions given by the owner
1493
-							if ($mount instanceof MoveableMount) {
1494
-								$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1495
-							} else {
1496
-								$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1497
-							}
1498
-
1499
-							//remove any existing entry with the same name
1500
-							foreach ($files as $i => $file) {
1501
-								if ($file['name'] === $rootEntry['name']) {
1502
-									unset($files[$i]);
1503
-									break;
1504
-								}
1505
-							}
1506
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507
-
1508
-							// if sharing was disabled for the user we remove the share permissions
1509
-							if (\OCP\Util::isSharingDisabledForUser()) {
1510
-								$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1511
-							}
1512
-
1513
-							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515
-						}
1516
-					}
1517
-				}
1518
-			}
1519
-
1520
-			if ($mimetype_filter) {
1521
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1522
-					if (strpos($mimetype_filter, '/')) {
1523
-						return $file->getMimetype() === $mimetype_filter;
1524
-					} else {
1525
-						return $file->getMimePart() === $mimetype_filter;
1526
-					}
1527
-				});
1528
-			}
1529
-
1530
-			return $files;
1531
-		} else {
1532
-			return [];
1533
-		}
1534
-	}
1535
-
1536
-	/**
1537
-	 * change file metadata
1538
-	 *
1539
-	 * @param string $path
1540
-	 * @param array|\OCP\Files\FileInfo $data
1541
-	 * @return int
1542
-	 *
1543
-	 * returns the fileid of the updated file
1544
-	 */
1545
-	public function putFileInfo($path, $data) {
1546
-		$this->assertPathLength($path);
1547
-		if ($data instanceof FileInfo) {
1548
-			$data = $data->getData();
1549
-		}
1550
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1551
-		/**
1552
-		 * @var \OC\Files\Storage\Storage $storage
1553
-		 * @var string $internalPath
1554
-		 */
1555
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
1556
-		if ($storage) {
1557
-			$cache = $storage->getCache($path);
1558
-
1559
-			if (!$cache->inCache($internalPath)) {
1560
-				$scanner = $storage->getScanner($internalPath);
1561
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1562
-			}
1563
-
1564
-			return $cache->put($internalPath, $data);
1565
-		} else {
1566
-			return -1;
1567
-		}
1568
-	}
1569
-
1570
-	/**
1571
-	 * search for files with the name matching $query
1572
-	 *
1573
-	 * @param string $query
1574
-	 * @return FileInfo[]
1575
-	 */
1576
-	public function search($query) {
1577
-		return $this->searchCommon('search', array('%' . $query . '%'));
1578
-	}
1579
-
1580
-	/**
1581
-	 * search for files with the name matching $query
1582
-	 *
1583
-	 * @param string $query
1584
-	 * @return FileInfo[]
1585
-	 */
1586
-	public function searchRaw($query) {
1587
-		return $this->searchCommon('search', array($query));
1588
-	}
1589
-
1590
-	/**
1591
-	 * search for files by mimetype
1592
-	 *
1593
-	 * @param string $mimetype
1594
-	 * @return FileInfo[]
1595
-	 */
1596
-	public function searchByMime($mimetype) {
1597
-		return $this->searchCommon('searchByMime', array($mimetype));
1598
-	}
1599
-
1600
-	/**
1601
-	 * search for files by tag
1602
-	 *
1603
-	 * @param string|int $tag name or tag id
1604
-	 * @param string $userId owner of the tags
1605
-	 * @return FileInfo[]
1606
-	 */
1607
-	public function searchByTag($tag, $userId) {
1608
-		return $this->searchCommon('searchByTag', array($tag, $userId));
1609
-	}
1610
-
1611
-	/**
1612
-	 * @param string $method cache method
1613
-	 * @param array $args
1614
-	 * @return FileInfo[]
1615
-	 */
1616
-	private function searchCommon($method, $args) {
1617
-		$files = array();
1618
-		$rootLength = strlen($this->fakeRoot);
1619
-
1620
-		$mount = $this->getMount('');
1621
-		$mountPoint = $mount->getMountPoint();
1622
-		$storage = $mount->getStorage();
1623
-		if ($storage) {
1624
-			$cache = $storage->getCache('');
1625
-
1626
-			$results = call_user_func_array(array($cache, $method), $args);
1627
-			foreach ($results as $result) {
1628
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1629
-					$internalPath = $result['path'];
1630
-					$path = $mountPoint . $result['path'];
1631
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1632
-					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633
-					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634
-				}
1635
-			}
1636
-
1637
-			$mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1638
-			foreach ($mounts as $mount) {
1639
-				$mountPoint = $mount->getMountPoint();
1640
-				$storage = $mount->getStorage();
1641
-				if ($storage) {
1642
-					$cache = $storage->getCache('');
1643
-
1644
-					$relativeMountPoint = substr($mountPoint, $rootLength);
1645
-					$results = call_user_func_array(array($cache, $method), $args);
1646
-					if ($results) {
1647
-						foreach ($results as $result) {
1648
-							$internalPath = $result['path'];
1649
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
-							$path = rtrim($mountPoint . $internalPath, '/');
1651
-							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652
-							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653
-						}
1654
-					}
1655
-				}
1656
-			}
1657
-		}
1658
-		return $files;
1659
-	}
1660
-
1661
-	/**
1662
-	 * Get the owner for a file or folder
1663
-	 *
1664
-	 * @param string $path
1665
-	 * @return string the user id of the owner
1666
-	 * @throws NotFoundException
1667
-	 */
1668
-	public function getOwner($path) {
1669
-		$info = $this->getFileInfo($path);
1670
-		if (!$info) {
1671
-			throw new NotFoundException($path . ' not found while trying to get owner');
1672
-		}
1673
-		return $info->getOwner()->getUID();
1674
-	}
1675
-
1676
-	/**
1677
-	 * get the ETag for a file or folder
1678
-	 *
1679
-	 * @param string $path
1680
-	 * @return string
1681
-	 */
1682
-	public function getETag($path) {
1683
-		/**
1684
-		 * @var Storage\Storage $storage
1685
-		 * @var string $internalPath
1686
-		 */
1687
-		list($storage, $internalPath) = $this->resolvePath($path);
1688
-		if ($storage) {
1689
-			return $storage->getETag($internalPath);
1690
-		} else {
1691
-			return null;
1692
-		}
1693
-	}
1694
-
1695
-	/**
1696
-	 * Get the path of a file by id, relative to the view
1697
-	 *
1698
-	 * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1699
-	 *
1700
-	 * @param int $id
1701
-	 * @throws NotFoundException
1702
-	 * @return string
1703
-	 */
1704
-	public function getPath($id) {
1705
-		$id = (int)$id;
1706
-		$manager = Filesystem::getMountManager();
1707
-		$mounts = $manager->findIn($this->fakeRoot);
1708
-		$mounts[] = $manager->find($this->fakeRoot);
1709
-		// reverse the array so we start with the storage this view is in
1710
-		// which is the most likely to contain the file we're looking for
1711
-		$mounts = array_reverse($mounts);
1712
-		foreach ($mounts as $mount) {
1713
-			/**
1714
-			 * @var \OC\Files\Mount\MountPoint $mount
1715
-			 */
1716
-			if ($mount->getStorage()) {
1717
-				$cache = $mount->getStorage()->getCache();
1718
-				$internalPath = $cache->getPathById($id);
1719
-				if (is_string($internalPath)) {
1720
-					$fullPath = $mount->getMountPoint() . $internalPath;
1721
-					if (!is_null($path = $this->getRelativePath($fullPath))) {
1722
-						return $path;
1723
-					}
1724
-				}
1725
-			}
1726
-		}
1727
-		throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1728
-	}
1729
-
1730
-	/**
1731
-	 * @param string $path
1732
-	 * @throws InvalidPathException
1733
-	 */
1734
-	private function assertPathLength($path) {
1735
-		$maxLen = min(PHP_MAXPATHLEN, 4000);
1736
-		// Check for the string length - performed using isset() instead of strlen()
1737
-		// because isset() is about 5x-40x faster.
1738
-		if (isset($path[$maxLen])) {
1739
-			$pathLen = strlen($path);
1740
-			throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1741
-		}
1742
-	}
1743
-
1744
-	/**
1745
-	 * check if it is allowed to move a mount point to a given target.
1746
-	 * It is not allowed to move a mount point into a different mount point or
1747
-	 * into an already shared folder
1748
-	 *
1749
-	 * @param string $target path
1750
-	 * @return boolean
1751
-	 */
1752
-	private function isTargetAllowed($target) {
1753
-
1754
-		list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1755
-		if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1756
-			\OCP\Util::writeLog('files',
1757
-				'It is not allowed to move one mount point into another one',
1758
-				\OCP\Util::DEBUG);
1759
-			return false;
1760
-		}
1761
-
1762
-		// note: cannot use the view because the target is already locked
1763
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1764
-		if ($fileId === -1) {
1765
-			// target might not exist, need to check parent instead
1766
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1767
-		}
1768
-
1769
-		// check if any of the parents were shared by the current owner (include collections)
1770
-		$shares = \OCP\Share::getItemShared(
1771
-			'folder',
1772
-			$fileId,
1773
-			\OCP\Share::FORMAT_NONE,
1774
-			null,
1775
-			true
1776
-		);
1777
-
1778
-		if (count($shares) > 0) {
1779
-			\OCP\Util::writeLog('files',
1780
-				'It is not allowed to move one mount point into a shared folder',
1781
-				\OCP\Util::DEBUG);
1782
-			return false;
1783
-		}
1784
-
1785
-		return true;
1786
-	}
1787
-
1788
-	/**
1789
-	 * Get a fileinfo object for files that are ignored in the cache (part files)
1790
-	 *
1791
-	 * @param string $path
1792
-	 * @return \OCP\Files\FileInfo
1793
-	 */
1794
-	private function getPartFileInfo($path) {
1795
-		$mount = $this->getMount($path);
1796
-		$storage = $mount->getStorage();
1797
-		$internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1798
-		$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1799
-		return new FileInfo(
1800
-			$this->getAbsolutePath($path),
1801
-			$storage,
1802
-			$internalPath,
1803
-			[
1804
-				'fileid' => null,
1805
-				'mimetype' => $storage->getMimeType($internalPath),
1806
-				'name' => basename($path),
1807
-				'etag' => null,
1808
-				'size' => $storage->filesize($internalPath),
1809
-				'mtime' => $storage->filemtime($internalPath),
1810
-				'encrypted' => false,
1811
-				'permissions' => \OCP\Constants::PERMISSION_ALL
1812
-			],
1813
-			$mount,
1814
-			$owner
1815
-		);
1816
-	}
1817
-
1818
-	/**
1819
-	 * @param string $path
1820
-	 * @param string $fileName
1821
-	 * @throws InvalidPathException
1822
-	 */
1823
-	public function verifyPath($path, $fileName) {
1824
-		try {
1825
-			/** @type \OCP\Files\Storage $storage */
1826
-			list($storage, $internalPath) = $this->resolvePath($path);
1827
-			$storage->verifyPath($internalPath, $fileName);
1828
-		} catch (ReservedWordException $ex) {
1829
-			$l = \OC::$server->getL10N('lib');
1830
-			throw new InvalidPathException($l->t('File name is a reserved word'));
1831
-		} catch (InvalidCharacterInPathException $ex) {
1832
-			$l = \OC::$server->getL10N('lib');
1833
-			throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1834
-		} catch (FileNameTooLongException $ex) {
1835
-			$l = \OC::$server->getL10N('lib');
1836
-			throw new InvalidPathException($l->t('File name is too long'));
1837
-		} catch (InvalidDirectoryException $ex) {
1838
-			$l = \OC::$server->getL10N('lib');
1839
-			throw new InvalidPathException($l->t('Dot files are not allowed'));
1840
-		} catch (EmptyFileNameException $ex) {
1841
-			$l = \OC::$server->getL10N('lib');
1842
-			throw new InvalidPathException($l->t('Empty filename is not allowed'));
1843
-		}
1844
-	}
1845
-
1846
-	/**
1847
-	 * get all parent folders of $path
1848
-	 *
1849
-	 * @param string $path
1850
-	 * @return string[]
1851
-	 */
1852
-	private function getParents($path) {
1853
-		$path = trim($path, '/');
1854
-		if (!$path) {
1855
-			return [];
1856
-		}
1857
-
1858
-		$parts = explode('/', $path);
1859
-
1860
-		// remove the single file
1861
-		array_pop($parts);
1862
-		$result = array('/');
1863
-		$resultPath = '';
1864
-		foreach ($parts as $part) {
1865
-			if ($part) {
1866
-				$resultPath .= '/' . $part;
1867
-				$result[] = $resultPath;
1868
-			}
1869
-		}
1870
-		return $result;
1871
-	}
1872
-
1873
-	/**
1874
-	 * Returns the mount point for which to lock
1875
-	 *
1876
-	 * @param string $absolutePath absolute path
1877
-	 * @param bool $useParentMount true to return parent mount instead of whatever
1878
-	 * is mounted directly on the given path, false otherwise
1879
-	 * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1880
-	 */
1881
-	private function getMountForLock($absolutePath, $useParentMount = false) {
1882
-		$results = [];
1883
-		$mount = Filesystem::getMountManager()->find($absolutePath);
1884
-		if (!$mount) {
1885
-			return $results;
1886
-		}
1887
-
1888
-		if ($useParentMount) {
1889
-			// find out if something is mounted directly on the path
1890
-			$internalPath = $mount->getInternalPath($absolutePath);
1891
-			if ($internalPath === '') {
1892
-				// resolve the parent mount instead
1893
-				$mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1894
-			}
1895
-		}
1896
-
1897
-		return $mount;
1898
-	}
1899
-
1900
-	/**
1901
-	 * Lock the given path
1902
-	 *
1903
-	 * @param string $path the path of the file to lock, relative to the view
1904
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1905
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1906
-	 *
1907
-	 * @return bool False if the path is excluded from locking, true otherwise
1908
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1909
-	 */
1910
-	private function lockPath($path, $type, $lockMountPoint = false) {
1911
-		$absolutePath = $this->getAbsolutePath($path);
1912
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1913
-		if (!$this->shouldLockFile($absolutePath)) {
1914
-			return false;
1915
-		}
1916
-
1917
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1918
-		if ($mount) {
1919
-			try {
1920
-				$storage = $mount->getStorage();
1921
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1922
-					$storage->acquireLock(
1923
-						$mount->getInternalPath($absolutePath),
1924
-						$type,
1925
-						$this->lockingProvider
1926
-					);
1927
-				}
1928
-			} catch (\OCP\Lock\LockedException $e) {
1929
-				// rethrow with the a human-readable path
1930
-				throw new \OCP\Lock\LockedException(
1931
-					$this->getPathRelativeToFiles($absolutePath),
1932
-					$e
1933
-				);
1934
-			}
1935
-		}
1936
-
1937
-		return true;
1938
-	}
1939
-
1940
-	/**
1941
-	 * Change the lock type
1942
-	 *
1943
-	 * @param string $path the path of the file to lock, relative to the view
1944
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1945
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1946
-	 *
1947
-	 * @return bool False if the path is excluded from locking, true otherwise
1948
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1949
-	 */
1950
-	public function changeLock($path, $type, $lockMountPoint = false) {
1951
-		$path = Filesystem::normalizePath($path);
1952
-		$absolutePath = $this->getAbsolutePath($path);
1953
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1954
-		if (!$this->shouldLockFile($absolutePath)) {
1955
-			return false;
1956
-		}
1957
-
1958
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1959
-		if ($mount) {
1960
-			try {
1961
-				$storage = $mount->getStorage();
1962
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1963
-					$storage->changeLock(
1964
-						$mount->getInternalPath($absolutePath),
1965
-						$type,
1966
-						$this->lockingProvider
1967
-					);
1968
-				}
1969
-			} catch (\OCP\Lock\LockedException $e) {
1970
-				try {
1971
-					// rethrow with the a human-readable path
1972
-					throw new \OCP\Lock\LockedException(
1973
-						$this->getPathRelativeToFiles($absolutePath),
1974
-						$e
1975
-					);
1976
-				} catch (\InvalidArgumentException $e) {
1977
-					throw new \OCP\Lock\LockedException(
1978
-						$absolutePath,
1979
-						$e
1980
-					);
1981
-				}
1982
-			}
1983
-		}
1984
-
1985
-		return true;
1986
-	}
1987
-
1988
-	/**
1989
-	 * Unlock the given path
1990
-	 *
1991
-	 * @param string $path the path of the file to unlock, relative to the view
1992
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1993
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1994
-	 *
1995
-	 * @return bool False if the path is excluded from locking, true otherwise
1996
-	 */
1997
-	private function unlockPath($path, $type, $lockMountPoint = false) {
1998
-		$absolutePath = $this->getAbsolutePath($path);
1999
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2000
-		if (!$this->shouldLockFile($absolutePath)) {
2001
-			return false;
2002
-		}
2003
-
2004
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2005
-		if ($mount) {
2006
-			$storage = $mount->getStorage();
2007
-			if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2008
-				$storage->releaseLock(
2009
-					$mount->getInternalPath($absolutePath),
2010
-					$type,
2011
-					$this->lockingProvider
2012
-				);
2013
-			}
2014
-		}
2015
-
2016
-		return true;
2017
-	}
2018
-
2019
-	/**
2020
-	 * Lock a path and all its parents up to the root of the view
2021
-	 *
2022
-	 * @param string $path the path of the file to lock relative to the view
2023
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2024
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2025
-	 *
2026
-	 * @return bool False if the path is excluded from locking, true otherwise
2027
-	 */
2028
-	public function lockFile($path, $type, $lockMountPoint = false) {
2029
-		$absolutePath = $this->getAbsolutePath($path);
2030
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2031
-		if (!$this->shouldLockFile($absolutePath)) {
2032
-			return false;
2033
-		}
2034
-
2035
-		$this->lockPath($path, $type, $lockMountPoint);
2036
-
2037
-		$parents = $this->getParents($path);
2038
-		foreach ($parents as $parent) {
2039
-			$this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2040
-		}
2041
-
2042
-		return true;
2043
-	}
2044
-
2045
-	/**
2046
-	 * Unlock a path and all its parents up to the root of the view
2047
-	 *
2048
-	 * @param string $path the path of the file to lock relative to the view
2049
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2050
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2051
-	 *
2052
-	 * @return bool False if the path is excluded from locking, true otherwise
2053
-	 */
2054
-	public function unlockFile($path, $type, $lockMountPoint = false) {
2055
-		$absolutePath = $this->getAbsolutePath($path);
2056
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2057
-		if (!$this->shouldLockFile($absolutePath)) {
2058
-			return false;
2059
-		}
2060
-
2061
-		$this->unlockPath($path, $type, $lockMountPoint);
2062
-
2063
-		$parents = $this->getParents($path);
2064
-		foreach ($parents as $parent) {
2065
-			$this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2066
-		}
2067
-
2068
-		return true;
2069
-	}
2070
-
2071
-	/**
2072
-	 * Only lock files in data/user/files/
2073
-	 *
2074
-	 * @param string $path Absolute path to the file/folder we try to (un)lock
2075
-	 * @return bool
2076
-	 */
2077
-	protected function shouldLockFile($path) {
2078
-		$path = Filesystem::normalizePath($path);
2079
-
2080
-		$pathSegments = explode('/', $path);
2081
-		if (isset($pathSegments[2])) {
2082
-			// E.g.: /username/files/path-to-file
2083
-			return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2084
-		}
2085
-
2086
-		return strpos($path, '/appdata_') !== 0;
2087
-	}
2088
-
2089
-	/**
2090
-	 * Shortens the given absolute path to be relative to
2091
-	 * "$user/files".
2092
-	 *
2093
-	 * @param string $absolutePath absolute path which is under "files"
2094
-	 *
2095
-	 * @return string path relative to "files" with trimmed slashes or null
2096
-	 * if the path was NOT relative to files
2097
-	 *
2098
-	 * @throws \InvalidArgumentException if the given path was not under "files"
2099
-	 * @since 8.1.0
2100
-	 */
2101
-	public function getPathRelativeToFiles($absolutePath) {
2102
-		$path = Filesystem::normalizePath($absolutePath);
2103
-		$parts = explode('/', trim($path, '/'), 3);
2104
-		// "$user", "files", "path/to/dir"
2105
-		if (!isset($parts[1]) || $parts[1] !== 'files') {
2106
-			$this->logger->error(
2107
-				'$absolutePath must be relative to "files", value is "%s"',
2108
-				[
2109
-					$absolutePath
2110
-				]
2111
-			);
2112
-			throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2113
-		}
2114
-		if (isset($parts[2])) {
2115
-			return $parts[2];
2116
-		}
2117
-		return '';
2118
-	}
2119
-
2120
-	/**
2121
-	 * @param string $filename
2122
-	 * @return array
2123
-	 * @throws \OC\User\NoUserException
2124
-	 * @throws NotFoundException
2125
-	 */
2126
-	public function getUidAndFilename($filename) {
2127
-		$info = $this->getFileInfo($filename);
2128
-		if (!$info instanceof \OCP\Files\FileInfo) {
2129
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2130
-		}
2131
-		$uid = $info->getOwner()->getUID();
2132
-		if ($uid != \OCP\User::getUser()) {
2133
-			Filesystem::initMountPoints($uid);
2134
-			$ownerView = new View('/' . $uid . '/files');
2135
-			try {
2136
-				$filename = $ownerView->getPath($info['fileid']);
2137
-			} catch (NotFoundException $e) {
2138
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2139
-			}
2140
-		}
2141
-		return [$uid, $filename];
2142
-	}
2143
-
2144
-	/**
2145
-	 * Creates parent non-existing folders
2146
-	 *
2147
-	 * @param string $filePath
2148
-	 * @return bool
2149
-	 */
2150
-	private function createParentDirectories($filePath) {
2151
-		$directoryParts = explode('/', $filePath);
2152
-		$directoryParts = array_filter($directoryParts);
2153
-		foreach ($directoryParts as $key => $part) {
2154
-			$currentPathElements = array_slice($directoryParts, 0, $key);
2155
-			$currentPath = '/' . implode('/', $currentPathElements);
2156
-			if ($this->is_file($currentPath)) {
2157
-				return false;
2158
-			}
2159
-			if (!$this->file_exists($currentPath)) {
2160
-				$this->mkdir($currentPath);
2161
-			}
2162
-		}
2163
-
2164
-		return true;
2165
-	}
83
+    /** @var string */
84
+    private $fakeRoot = '';
85
+
86
+    /**
87
+     * @var \OCP\Lock\ILockingProvider
88
+     */
89
+    protected $lockingProvider;
90
+
91
+    private $lockingEnabled;
92
+
93
+    private $updaterEnabled = true;
94
+
95
+    /** @var \OC\User\Manager */
96
+    private $userManager;
97
+
98
+    /** @var \OCP\ILogger */
99
+    private $logger;
100
+
101
+    /**
102
+     * @param string $root
103
+     * @throws \Exception If $root contains an invalid path
104
+     */
105
+    public function __construct($root = '') {
106
+        if (is_null($root)) {
107
+            throw new \InvalidArgumentException('Root can\'t be null');
108
+        }
109
+        if (!Filesystem::isValidPath($root)) {
110
+            throw new \Exception();
111
+        }
112
+
113
+        $this->fakeRoot = $root;
114
+        $this->lockingProvider = \OC::$server->getLockingProvider();
115
+        $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
116
+        $this->userManager = \OC::$server->getUserManager();
117
+        $this->logger = \OC::$server->getLogger();
118
+    }
119
+
120
+    public function getAbsolutePath($path = '/') {
121
+        if ($path === null) {
122
+            return null;
123
+        }
124
+        $this->assertPathLength($path);
125
+        if ($path === '') {
126
+            $path = '/';
127
+        }
128
+        if ($path[0] !== '/') {
129
+            $path = '/' . $path;
130
+        }
131
+        return $this->fakeRoot . $path;
132
+    }
133
+
134
+    /**
135
+     * change the root to a fake root
136
+     *
137
+     * @param string $fakeRoot
138
+     * @return boolean|null
139
+     */
140
+    public function chroot($fakeRoot) {
141
+        if (!$fakeRoot == '') {
142
+            if ($fakeRoot[0] !== '/') {
143
+                $fakeRoot = '/' . $fakeRoot;
144
+            }
145
+        }
146
+        $this->fakeRoot = $fakeRoot;
147
+    }
148
+
149
+    /**
150
+     * get the fake root
151
+     *
152
+     * @return string
153
+     */
154
+    public function getRoot() {
155
+        return $this->fakeRoot;
156
+    }
157
+
158
+    /**
159
+     * get path relative to the root of the view
160
+     *
161
+     * @param string $path
162
+     * @return string
163
+     */
164
+    public function getRelativePath($path) {
165
+        $this->assertPathLength($path);
166
+        if ($this->fakeRoot == '') {
167
+            return $path;
168
+        }
169
+
170
+        if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
171
+            return '/';
172
+        }
173
+
174
+        // missing slashes can cause wrong matches!
175
+        $root = rtrim($this->fakeRoot, '/') . '/';
176
+
177
+        if (strpos($path, $root) !== 0) {
178
+            return null;
179
+        } else {
180
+            $path = substr($path, strlen($this->fakeRoot));
181
+            if (strlen($path) === 0) {
182
+                return '/';
183
+            } else {
184
+                return $path;
185
+            }
186
+        }
187
+    }
188
+
189
+    /**
190
+     * get the mountpoint of the storage object for a path
191
+     * ( note: because a storage is not always mounted inside the fakeroot, the
192
+     * returned mountpoint is relative to the absolute root of the filesystem
193
+     * and does not take the chroot into account )
194
+     *
195
+     * @param string $path
196
+     * @return string
197
+     */
198
+    public function getMountPoint($path) {
199
+        return Filesystem::getMountPoint($this->getAbsolutePath($path));
200
+    }
201
+
202
+    /**
203
+     * get the mountpoint of the storage object for a path
204
+     * ( note: because a storage is not always mounted inside the fakeroot, the
205
+     * returned mountpoint is relative to the absolute root of the filesystem
206
+     * and does not take the chroot into account )
207
+     *
208
+     * @param string $path
209
+     * @return \OCP\Files\Mount\IMountPoint
210
+     */
211
+    public function getMount($path) {
212
+        return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
213
+    }
214
+
215
+    /**
216
+     * resolve a path to a storage and internal path
217
+     *
218
+     * @param string $path
219
+     * @return array an array consisting of the storage and the internal path
220
+     */
221
+    public function resolvePath($path) {
222
+        $a = $this->getAbsolutePath($path);
223
+        $p = Filesystem::normalizePath($a);
224
+        return Filesystem::resolvePath($p);
225
+    }
226
+
227
+    /**
228
+     * return the path to a local version of the file
229
+     * we need this because we can't know if a file is stored local or not from
230
+     * outside the filestorage and for some purposes a local file is needed
231
+     *
232
+     * @param string $path
233
+     * @return string
234
+     */
235
+    public function getLocalFile($path) {
236
+        $parent = substr($path, 0, strrpos($path, '/'));
237
+        $path = $this->getAbsolutePath($path);
238
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
239
+        if (Filesystem::isValidPath($parent) and $storage) {
240
+            return $storage->getLocalFile($internalPath);
241
+        } else {
242
+            return null;
243
+        }
244
+    }
245
+
246
+    /**
247
+     * @param string $path
248
+     * @return string
249
+     */
250
+    public function getLocalFolder($path) {
251
+        $parent = substr($path, 0, strrpos($path, '/'));
252
+        $path = $this->getAbsolutePath($path);
253
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
254
+        if (Filesystem::isValidPath($parent) and $storage) {
255
+            return $storage->getLocalFolder($internalPath);
256
+        } else {
257
+            return null;
258
+        }
259
+    }
260
+
261
+    /**
262
+     * the following functions operate with arguments and return values identical
263
+     * to those of their PHP built-in equivalents. Mostly they are merely wrappers
264
+     * for \OC\Files\Storage\Storage via basicOperation().
265
+     */
266
+    public function mkdir($path) {
267
+        return $this->basicOperation('mkdir', $path, array('create', 'write'));
268
+    }
269
+
270
+    /**
271
+     * remove mount point
272
+     *
273
+     * @param \OC\Files\Mount\MoveableMount $mount
274
+     * @param string $path relative to data/
275
+     * @return boolean
276
+     */
277
+    protected function removeMount($mount, $path) {
278
+        if ($mount instanceof MoveableMount) {
279
+            // cut of /user/files to get the relative path to data/user/files
280
+            $pathParts = explode('/', $path, 4);
281
+            $relPath = '/' . $pathParts[3];
282
+            $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283
+            \OC_Hook::emit(
284
+                Filesystem::CLASSNAME, "umount",
285
+                array(Filesystem::signal_param_path => $relPath)
286
+            );
287
+            $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
288
+            $result = $mount->removeMount();
289
+            $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
290
+            if ($result) {
291
+                \OC_Hook::emit(
292
+                    Filesystem::CLASSNAME, "post_umount",
293
+                    array(Filesystem::signal_param_path => $relPath)
294
+                );
295
+            }
296
+            $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
297
+            return $result;
298
+        } else {
299
+            // do not allow deleting the storage's root / the mount point
300
+            // because for some storages it might delete the whole contents
301
+            // but isn't supposed to work that way
302
+            return false;
303
+        }
304
+    }
305
+
306
+    public function disableCacheUpdate() {
307
+        $this->updaterEnabled = false;
308
+    }
309
+
310
+    public function enableCacheUpdate() {
311
+        $this->updaterEnabled = true;
312
+    }
313
+
314
+    protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
315
+        if ($this->updaterEnabled) {
316
+            if (is_null($time)) {
317
+                $time = time();
318
+            }
319
+            $storage->getUpdater()->update($internalPath, $time);
320
+        }
321
+    }
322
+
323
+    protected function removeUpdate(Storage $storage, $internalPath) {
324
+        if ($this->updaterEnabled) {
325
+            $storage->getUpdater()->remove($internalPath);
326
+        }
327
+    }
328
+
329
+    protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
330
+        if ($this->updaterEnabled) {
331
+            $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
332
+        }
333
+    }
334
+
335
+    /**
336
+     * @param string $path
337
+     * @return bool|mixed
338
+     */
339
+    public function rmdir($path) {
340
+        $absolutePath = $this->getAbsolutePath($path);
341
+        $mount = Filesystem::getMountManager()->find($absolutePath);
342
+        if ($mount->getInternalPath($absolutePath) === '') {
343
+            return $this->removeMount($mount, $absolutePath);
344
+        }
345
+        if ($this->is_dir($path)) {
346
+            $result = $this->basicOperation('rmdir', $path, array('delete'));
347
+        } else {
348
+            $result = false;
349
+        }
350
+
351
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
352
+            $storage = $mount->getStorage();
353
+            $internalPath = $mount->getInternalPath($absolutePath);
354
+            $storage->getUpdater()->remove($internalPath);
355
+        }
356
+        return $result;
357
+    }
358
+
359
+    /**
360
+     * @param string $path
361
+     * @return resource
362
+     */
363
+    public function opendir($path) {
364
+        return $this->basicOperation('opendir', $path, array('read'));
365
+    }
366
+
367
+    /**
368
+     * @param string $path
369
+     * @return bool|mixed
370
+     */
371
+    public function is_dir($path) {
372
+        if ($path == '/') {
373
+            return true;
374
+        }
375
+        return $this->basicOperation('is_dir', $path);
376
+    }
377
+
378
+    /**
379
+     * @param string $path
380
+     * @return bool|mixed
381
+     */
382
+    public function is_file($path) {
383
+        if ($path == '/') {
384
+            return false;
385
+        }
386
+        return $this->basicOperation('is_file', $path);
387
+    }
388
+
389
+    /**
390
+     * @param string $path
391
+     * @return mixed
392
+     */
393
+    public function stat($path) {
394
+        return $this->basicOperation('stat', $path);
395
+    }
396
+
397
+    /**
398
+     * @param string $path
399
+     * @return mixed
400
+     */
401
+    public function filetype($path) {
402
+        return $this->basicOperation('filetype', $path);
403
+    }
404
+
405
+    /**
406
+     * @param string $path
407
+     * @return mixed
408
+     */
409
+    public function filesize($path) {
410
+        return $this->basicOperation('filesize', $path);
411
+    }
412
+
413
+    /**
414
+     * @param string $path
415
+     * @return bool|mixed
416
+     * @throws \OCP\Files\InvalidPathException
417
+     */
418
+    public function readfile($path) {
419
+        $this->assertPathLength($path);
420
+        @ob_end_clean();
421
+        $handle = $this->fopen($path, 'rb');
422
+        if ($handle) {
423
+            $chunkSize = 8192; // 8 kB chunks
424
+            while (!feof($handle)) {
425
+                echo fread($handle, $chunkSize);
426
+                flush();
427
+            }
428
+            fclose($handle);
429
+            $size = $this->filesize($path);
430
+            return $size;
431
+        }
432
+        return false;
433
+    }
434
+
435
+    /**
436
+     * @param string $path
437
+     * @param int $from
438
+     * @param int $to
439
+     * @return bool|mixed
440
+     * @throws \OCP\Files\InvalidPathException
441
+     * @throws \OCP\Files\UnseekableException
442
+     */
443
+    public function readfilePart($path, $from, $to) {
444
+        $this->assertPathLength($path);
445
+        @ob_end_clean();
446
+        $handle = $this->fopen($path, 'rb');
447
+        if ($handle) {
448
+            $chunkSize = 8192; // 8 kB chunks
449
+            $startReading = true;
450
+
451
+            if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
452
+                // forward file handle via chunked fread because fseek seem to have failed
453
+
454
+                $end = $from + 1;
455
+                while (!feof($handle) && ftell($handle) < $end) {
456
+                    $len = $from - ftell($handle);
457
+                    if ($len > $chunkSize) {
458
+                        $len = $chunkSize;
459
+                    }
460
+                    $result = fread($handle, $len);
461
+
462
+                    if ($result === false) {
463
+                        $startReading = false;
464
+                        break;
465
+                    }
466
+                }
467
+            }
468
+
469
+            if ($startReading) {
470
+                $end = $to + 1;
471
+                while (!feof($handle) && ftell($handle) < $end) {
472
+                    $len = $end - ftell($handle);
473
+                    if ($len > $chunkSize) {
474
+                        $len = $chunkSize;
475
+                    }
476
+                    echo fread($handle, $len);
477
+                    flush();
478
+                }
479
+                $size = ftell($handle) - $from;
480
+                return $size;
481
+            }
482
+
483
+            throw new \OCP\Files\UnseekableException('fseek error');
484
+        }
485
+        return false;
486
+    }
487
+
488
+    /**
489
+     * @param string $path
490
+     * @return mixed
491
+     */
492
+    public function isCreatable($path) {
493
+        return $this->basicOperation('isCreatable', $path);
494
+    }
495
+
496
+    /**
497
+     * @param string $path
498
+     * @return mixed
499
+     */
500
+    public function isReadable($path) {
501
+        return $this->basicOperation('isReadable', $path);
502
+    }
503
+
504
+    /**
505
+     * @param string $path
506
+     * @return mixed
507
+     */
508
+    public function isUpdatable($path) {
509
+        return $this->basicOperation('isUpdatable', $path);
510
+    }
511
+
512
+    /**
513
+     * @param string $path
514
+     * @return bool|mixed
515
+     */
516
+    public function isDeletable($path) {
517
+        $absolutePath = $this->getAbsolutePath($path);
518
+        $mount = Filesystem::getMountManager()->find($absolutePath);
519
+        if ($mount->getInternalPath($absolutePath) === '') {
520
+            return $mount instanceof MoveableMount;
521
+        }
522
+        return $this->basicOperation('isDeletable', $path);
523
+    }
524
+
525
+    /**
526
+     * @param string $path
527
+     * @return mixed
528
+     */
529
+    public function isSharable($path) {
530
+        return $this->basicOperation('isSharable', $path);
531
+    }
532
+
533
+    /**
534
+     * @param string $path
535
+     * @return bool|mixed
536
+     */
537
+    public function file_exists($path) {
538
+        if ($path == '/') {
539
+            return true;
540
+        }
541
+        return $this->basicOperation('file_exists', $path);
542
+    }
543
+
544
+    /**
545
+     * @param string $path
546
+     * @return mixed
547
+     */
548
+    public function filemtime($path) {
549
+        return $this->basicOperation('filemtime', $path);
550
+    }
551
+
552
+    /**
553
+     * @param string $path
554
+     * @param int|string $mtime
555
+     * @return bool
556
+     */
557
+    public function touch($path, $mtime = null) {
558
+        if (!is_null($mtime) and !is_numeric($mtime)) {
559
+            $mtime = strtotime($mtime);
560
+        }
561
+
562
+        $hooks = array('touch');
563
+
564
+        if (!$this->file_exists($path)) {
565
+            $hooks[] = 'create';
566
+            $hooks[] = 'write';
567
+        }
568
+        $result = $this->basicOperation('touch', $path, $hooks, $mtime);
569
+        if (!$result) {
570
+            // If create file fails because of permissions on external storage like SMB folders,
571
+            // check file exists and return false if not.
572
+            if (!$this->file_exists($path)) {
573
+                return false;
574
+            }
575
+            if (is_null($mtime)) {
576
+                $mtime = time();
577
+            }
578
+            //if native touch fails, we emulate it by changing the mtime in the cache
579
+            $this->putFileInfo($path, array('mtime' => floor($mtime)));
580
+        }
581
+        return true;
582
+    }
583
+
584
+    /**
585
+     * @param string $path
586
+     * @return mixed
587
+     */
588
+    public function file_get_contents($path) {
589
+        return $this->basicOperation('file_get_contents', $path, array('read'));
590
+    }
591
+
592
+    /**
593
+     * @param bool $exists
594
+     * @param string $path
595
+     * @param bool $run
596
+     */
597
+    protected function emit_file_hooks_pre($exists, $path, &$run) {
598
+        if (!$exists) {
599
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
600
+                Filesystem::signal_param_path => $this->getHookPath($path),
601
+                Filesystem::signal_param_run => &$run,
602
+            ));
603
+        } else {
604
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
605
+                Filesystem::signal_param_path => $this->getHookPath($path),
606
+                Filesystem::signal_param_run => &$run,
607
+            ));
608
+        }
609
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
610
+            Filesystem::signal_param_path => $this->getHookPath($path),
611
+            Filesystem::signal_param_run => &$run,
612
+        ));
613
+    }
614
+
615
+    /**
616
+     * @param bool $exists
617
+     * @param string $path
618
+     */
619
+    protected function emit_file_hooks_post($exists, $path) {
620
+        if (!$exists) {
621
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
622
+                Filesystem::signal_param_path => $this->getHookPath($path),
623
+            ));
624
+        } else {
625
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
626
+                Filesystem::signal_param_path => $this->getHookPath($path),
627
+            ));
628
+        }
629
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
630
+            Filesystem::signal_param_path => $this->getHookPath($path),
631
+        ));
632
+    }
633
+
634
+    /**
635
+     * @param string $path
636
+     * @param mixed $data
637
+     * @return bool|mixed
638
+     * @throws \Exception
639
+     */
640
+    public function file_put_contents($path, $data) {
641
+        if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
642
+            $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
643
+            if (Filesystem::isValidPath($path)
644
+                and !Filesystem::isFileBlacklisted($path)
645
+            ) {
646
+                $path = $this->getRelativePath($absolutePath);
647
+
648
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
649
+
650
+                $exists = $this->file_exists($path);
651
+                $run = true;
652
+                if ($this->shouldEmitHooks($path)) {
653
+                    $this->emit_file_hooks_pre($exists, $path, $run);
654
+                }
655
+                if (!$run) {
656
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
657
+                    return false;
658
+                }
659
+
660
+                $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
661
+
662
+                /** @var \OC\Files\Storage\Storage $storage */
663
+                list($storage, $internalPath) = $this->resolvePath($path);
664
+                $target = $storage->fopen($internalPath, 'w');
665
+                if ($target) {
666
+                    list (, $result) = \OC_Helper::streamCopy($data, $target);
667
+                    fclose($target);
668
+                    fclose($data);
669
+
670
+                    $this->writeUpdate($storage, $internalPath);
671
+
672
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
673
+
674
+                    if ($this->shouldEmitHooks($path) && $result !== false) {
675
+                        $this->emit_file_hooks_post($exists, $path);
676
+                    }
677
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
678
+                    return $result;
679
+                } else {
680
+                    $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
681
+                    return false;
682
+                }
683
+            } else {
684
+                return false;
685
+            }
686
+        } else {
687
+            $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
688
+            return $this->basicOperation('file_put_contents', $path, $hooks, $data);
689
+        }
690
+    }
691
+
692
+    /**
693
+     * @param string $path
694
+     * @return bool|mixed
695
+     */
696
+    public function unlink($path) {
697
+        if ($path === '' || $path === '/') {
698
+            // do not allow deleting the root
699
+            return false;
700
+        }
701
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
702
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
+        $mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
704
+        if ($mount and $mount->getInternalPath($absolutePath) === '') {
705
+            return $this->removeMount($mount, $absolutePath);
706
+        }
707
+        if ($this->is_dir($path)) {
708
+            $result = $this->basicOperation('rmdir', $path, ['delete']);
709
+        } else {
710
+            $result = $this->basicOperation('unlink', $path, ['delete']);
711
+        }
712
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
713
+            $storage = $mount->getStorage();
714
+            $internalPath = $mount->getInternalPath($absolutePath);
715
+            $storage->getUpdater()->remove($internalPath);
716
+            return true;
717
+        } else {
718
+            return $result;
719
+        }
720
+    }
721
+
722
+    /**
723
+     * @param string $directory
724
+     * @return bool|mixed
725
+     */
726
+    public function deleteAll($directory) {
727
+        return $this->rmdir($directory);
728
+    }
729
+
730
+    /**
731
+     * Rename/move a file or folder from the source path to target path.
732
+     *
733
+     * @param string $path1 source path
734
+     * @param string $path2 target path
735
+     *
736
+     * @return bool|mixed
737
+     */
738
+    public function rename($path1, $path2) {
739
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
740
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
741
+        $result = false;
742
+        if (
743
+            Filesystem::isValidPath($path2)
744
+            and Filesystem::isValidPath($path1)
745
+            and !Filesystem::isFileBlacklisted($path2)
746
+        ) {
747
+            $path1 = $this->getRelativePath($absolutePath1);
748
+            $path2 = $this->getRelativePath($absolutePath2);
749
+            $exists = $this->file_exists($path2);
750
+
751
+            if ($path1 == null or $path2 == null) {
752
+                return false;
753
+            }
754
+
755
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
756
+            try {
757
+                $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
758
+
759
+                $run = true;
760
+                if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
761
+                    // if it was a rename from a part file to a regular file it was a write and not a rename operation
762
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
763
+                } elseif ($this->shouldEmitHooks($path1)) {
764
+                    \OC_Hook::emit(
765
+                        Filesystem::CLASSNAME, Filesystem::signal_rename,
766
+                        array(
767
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
768
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
769
+                            Filesystem::signal_param_run => &$run
770
+                        )
771
+                    );
772
+                }
773
+                if ($run) {
774
+                    $this->verifyPath(dirname($path2), basename($path2));
775
+
776
+                    $manager = Filesystem::getMountManager();
777
+                    $mount1 = $this->getMount($path1);
778
+                    $mount2 = $this->getMount($path2);
779
+                    $storage1 = $mount1->getStorage();
780
+                    $storage2 = $mount2->getStorage();
781
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
782
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
783
+
784
+                    $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
785
+                    try {
786
+                        $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
787
+
788
+                        if ($internalPath1 === '') {
789
+                            if ($mount1 instanceof MoveableMount) {
790
+                                if ($this->isTargetAllowed($absolutePath2)) {
791
+                                    /**
792
+                                     * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
793
+                                     */
794
+                                    $sourceMountPoint = $mount1->getMountPoint();
795
+                                    $result = $mount1->moveMount($absolutePath2);
796
+                                    $manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
797
+                                } else {
798
+                                    $result = false;
799
+                                }
800
+                            } else {
801
+                                $result = false;
802
+                            }
803
+                            // moving a file/folder within the same mount point
804
+                        } elseif ($storage1 === $storage2) {
805
+                            if ($storage1) {
806
+                                $result = $storage1->rename($internalPath1, $internalPath2);
807
+                            } else {
808
+                                $result = false;
809
+                            }
810
+                            // moving a file/folder between storages (from $storage1 to $storage2)
811
+                        } else {
812
+                            $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
813
+                        }
814
+
815
+                        if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
816
+                            // if it was a rename from a part file to a regular file it was a write and not a rename operation
817
+                            $this->writeUpdate($storage2, $internalPath2);
818
+                        } else if ($result) {
819
+                            if ($internalPath1 !== '') { // don't do a cache update for moved mounts
820
+                                $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821
+                            }
822
+                        }
823
+                    } catch(\Exception $e) {
824
+                        throw $e;
825
+                    } finally {
826
+                        $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
827
+                        $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
828
+                    }
829
+
830
+                    if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
831
+                        if ($this->shouldEmitHooks()) {
832
+                            $this->emit_file_hooks_post($exists, $path2);
833
+                        }
834
+                    } elseif ($result) {
835
+                        if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
836
+                            \OC_Hook::emit(
837
+                                Filesystem::CLASSNAME,
838
+                                Filesystem::signal_post_rename,
839
+                                array(
840
+                                    Filesystem::signal_param_oldpath => $this->getHookPath($path1),
841
+                                    Filesystem::signal_param_newpath => $this->getHookPath($path2)
842
+                                )
843
+                            );
844
+                        }
845
+                    }
846
+                }
847
+            } catch(\Exception $e) {
848
+                throw $e;
849
+            } finally {
850
+                $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
851
+                $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
852
+            }
853
+        }
854
+        return $result;
855
+    }
856
+
857
+    /**
858
+     * Copy a file/folder from the source path to target path
859
+     *
860
+     * @param string $path1 source path
861
+     * @param string $path2 target path
862
+     * @param bool $preserveMtime whether to preserve mtime on the copy
863
+     *
864
+     * @return bool|mixed
865
+     */
866
+    public function copy($path1, $path2, $preserveMtime = false) {
867
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
868
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
869
+        $result = false;
870
+        if (
871
+            Filesystem::isValidPath($path2)
872
+            and Filesystem::isValidPath($path1)
873
+            and !Filesystem::isFileBlacklisted($path2)
874
+        ) {
875
+            $path1 = $this->getRelativePath($absolutePath1);
876
+            $path2 = $this->getRelativePath($absolutePath2);
877
+
878
+            if ($path1 == null or $path2 == null) {
879
+                return false;
880
+            }
881
+            $run = true;
882
+
883
+            $this->lockFile($path2, ILockingProvider::LOCK_SHARED);
884
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED);
885
+            $lockTypePath1 = ILockingProvider::LOCK_SHARED;
886
+            $lockTypePath2 = ILockingProvider::LOCK_SHARED;
887
+
888
+            try {
889
+
890
+                $exists = $this->file_exists($path2);
891
+                if ($this->shouldEmitHooks()) {
892
+                    \OC_Hook::emit(
893
+                        Filesystem::CLASSNAME,
894
+                        Filesystem::signal_copy,
895
+                        array(
896
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
897
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
898
+                            Filesystem::signal_param_run => &$run
899
+                        )
900
+                    );
901
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
902
+                }
903
+                if ($run) {
904
+                    $mount1 = $this->getMount($path1);
905
+                    $mount2 = $this->getMount($path2);
906
+                    $storage1 = $mount1->getStorage();
907
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
908
+                    $storage2 = $mount2->getStorage();
909
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
910
+
911
+                    $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
912
+                    $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
913
+
914
+                    if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
915
+                        if ($storage1) {
916
+                            $result = $storage1->copy($internalPath1, $internalPath2);
917
+                        } else {
918
+                            $result = false;
919
+                        }
920
+                    } else {
921
+                        $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
922
+                    }
923
+
924
+                    $this->writeUpdate($storage2, $internalPath2);
925
+
926
+                    $this->changeLock($path2, ILockingProvider::LOCK_SHARED);
927
+                    $lockTypePath2 = ILockingProvider::LOCK_SHARED;
928
+
929
+                    if ($this->shouldEmitHooks() && $result !== false) {
930
+                        \OC_Hook::emit(
931
+                            Filesystem::CLASSNAME,
932
+                            Filesystem::signal_post_copy,
933
+                            array(
934
+                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
935
+                                Filesystem::signal_param_newpath => $this->getHookPath($path2)
936
+                            )
937
+                        );
938
+                        $this->emit_file_hooks_post($exists, $path2);
939
+                    }
940
+
941
+                }
942
+            } catch (\Exception $e) {
943
+                $this->unlockFile($path2, $lockTypePath2);
944
+                $this->unlockFile($path1, $lockTypePath1);
945
+                throw $e;
946
+            }
947
+
948
+            $this->unlockFile($path2, $lockTypePath2);
949
+            $this->unlockFile($path1, $lockTypePath1);
950
+
951
+        }
952
+        return $result;
953
+    }
954
+
955
+    /**
956
+     * @param string $path
957
+     * @param string $mode 'r' or 'w'
958
+     * @return resource
959
+     */
960
+    public function fopen($path, $mode) {
961
+        $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
962
+        $hooks = array();
963
+        switch ($mode) {
964
+            case 'r':
965
+                $hooks[] = 'read';
966
+                break;
967
+            case 'r+':
968
+            case 'w+':
969
+            case 'x+':
970
+            case 'a+':
971
+                $hooks[] = 'read';
972
+                $hooks[] = 'write';
973
+                break;
974
+            case 'w':
975
+            case 'x':
976
+            case 'a':
977
+                $hooks[] = 'write';
978
+                break;
979
+            default:
980
+                \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
981
+        }
982
+
983
+        if ($mode !== 'r' && $mode !== 'w') {
984
+            \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');
985
+        }
986
+
987
+        return $this->basicOperation('fopen', $path, $hooks, $mode);
988
+    }
989
+
990
+    /**
991
+     * @param string $path
992
+     * @return bool|string
993
+     * @throws \OCP\Files\InvalidPathException
994
+     */
995
+    public function toTmpFile($path) {
996
+        $this->assertPathLength($path);
997
+        if (Filesystem::isValidPath($path)) {
998
+            $source = $this->fopen($path, 'r');
999
+            if ($source) {
1000
+                $extension = pathinfo($path, PATHINFO_EXTENSION);
1001
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1002
+                file_put_contents($tmpFile, $source);
1003
+                return $tmpFile;
1004
+            } else {
1005
+                return false;
1006
+            }
1007
+        } else {
1008
+            return false;
1009
+        }
1010
+    }
1011
+
1012
+    /**
1013
+     * @param string $tmpFile
1014
+     * @param string $path
1015
+     * @return bool|mixed
1016
+     * @throws \OCP\Files\InvalidPathException
1017
+     */
1018
+    public function fromTmpFile($tmpFile, $path) {
1019
+        $this->assertPathLength($path);
1020
+        if (Filesystem::isValidPath($path)) {
1021
+
1022
+            // Get directory that the file is going into
1023
+            $filePath = dirname($path);
1024
+
1025
+            // Create the directories if any
1026
+            if (!$this->file_exists($filePath)) {
1027
+                $result = $this->createParentDirectories($filePath);
1028
+                if ($result === false) {
1029
+                    return false;
1030
+                }
1031
+            }
1032
+
1033
+            $source = fopen($tmpFile, 'r');
1034
+            if ($source) {
1035
+                $result = $this->file_put_contents($path, $source);
1036
+                // $this->file_put_contents() might have already closed
1037
+                // the resource, so we check it, before trying to close it
1038
+                // to avoid messages in the error log.
1039
+                if (is_resource($source)) {
1040
+                    fclose($source);
1041
+                }
1042
+                unlink($tmpFile);
1043
+                return $result;
1044
+            } else {
1045
+                return false;
1046
+            }
1047
+        } else {
1048
+            return false;
1049
+        }
1050
+    }
1051
+
1052
+
1053
+    /**
1054
+     * @param string $path
1055
+     * @return mixed
1056
+     * @throws \OCP\Files\InvalidPathException
1057
+     */
1058
+    public function getMimeType($path) {
1059
+        $this->assertPathLength($path);
1060
+        return $this->basicOperation('getMimeType', $path);
1061
+    }
1062
+
1063
+    /**
1064
+     * @param string $type
1065
+     * @param string $path
1066
+     * @param bool $raw
1067
+     * @return bool|null|string
1068
+     */
1069
+    public function hash($type, $path, $raw = false) {
1070
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1071
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1072
+        if (Filesystem::isValidPath($path)) {
1073
+            $path = $this->getRelativePath($absolutePath);
1074
+            if ($path == null) {
1075
+                return false;
1076
+            }
1077
+            if ($this->shouldEmitHooks($path)) {
1078
+                \OC_Hook::emit(
1079
+                    Filesystem::CLASSNAME,
1080
+                    Filesystem::signal_read,
1081
+                    array(Filesystem::signal_param_path => $this->getHookPath($path))
1082
+                );
1083
+            }
1084
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1085
+            if ($storage) {
1086
+                $result = $storage->hash($type, $internalPath, $raw);
1087
+                return $result;
1088
+            }
1089
+        }
1090
+        return null;
1091
+    }
1092
+
1093
+    /**
1094
+     * @param string $path
1095
+     * @return mixed
1096
+     * @throws \OCP\Files\InvalidPathException
1097
+     */
1098
+    public function free_space($path = '/') {
1099
+        $this->assertPathLength($path);
1100
+        $result = $this->basicOperation('free_space', $path);
1101
+        if ($result === null) {
1102
+            throw new InvalidPathException();
1103
+        }
1104
+        return $result;
1105
+    }
1106
+
1107
+    /**
1108
+     * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1109
+     *
1110
+     * @param string $operation
1111
+     * @param string $path
1112
+     * @param array $hooks (optional)
1113
+     * @param mixed $extraParam (optional)
1114
+     * @return mixed
1115
+     * @throws \Exception
1116
+     *
1117
+     * This method takes requests for basic filesystem functions (e.g. reading & writing
1118
+     * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1119
+     * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1120
+     */
1121
+    private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1122
+        $postFix = (substr($path, -1, 1) === '/') ? '/' : '';
1123
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1124
+        if (Filesystem::isValidPath($path)
1125
+            and !Filesystem::isFileBlacklisted($path)
1126
+        ) {
1127
+            $path = $this->getRelativePath($absolutePath);
1128
+            if ($path == null) {
1129
+                return false;
1130
+            }
1131
+
1132
+            if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1133
+                // always a shared lock during pre-hooks so the hook can read the file
1134
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
1135
+            }
1136
+
1137
+            $run = $this->runHooks($hooks, $path);
1138
+            /** @var \OC\Files\Storage\Storage $storage */
1139
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1140
+            if ($run and $storage) {
1141
+                if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142
+                    $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1143
+                }
1144
+                try {
1145
+                    if (!is_null($extraParam)) {
1146
+                        $result = $storage->$operation($internalPath, $extraParam);
1147
+                    } else {
1148
+                        $result = $storage->$operation($internalPath);
1149
+                    }
1150
+                } catch (\Exception $e) {
1151
+                    if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1152
+                        $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1153
+                    } else if (in_array('read', $hooks)) {
1154
+                        $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1155
+                    }
1156
+                    throw $e;
1157
+                }
1158
+
1159
+                if ($result && in_array('delete', $hooks) and $result) {
1160
+                    $this->removeUpdate($storage, $internalPath);
1161
+                }
1162
+                if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1163
+                    $this->writeUpdate($storage, $internalPath);
1164
+                }
1165
+                if ($result && in_array('touch', $hooks)) {
1166
+                    $this->writeUpdate($storage, $internalPath, $extraParam);
1167
+                }
1168
+
1169
+                if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1170
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
1171
+                }
1172
+
1173
+                $unlockLater = false;
1174
+                if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1175
+                    $unlockLater = true;
1176
+                    // make sure our unlocking callback will still be called if connection is aborted
1177
+                    ignore_user_abort(true);
1178
+                    $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1179
+                        if (in_array('write', $hooks)) {
1180
+                            $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181
+                        } else if (in_array('read', $hooks)) {
1182
+                            $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1183
+                        }
1184
+                    });
1185
+                }
1186
+
1187
+                if ($this->shouldEmitHooks($path) && $result !== false) {
1188
+                    if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1189
+                        $this->runHooks($hooks, $path, true);
1190
+                    }
1191
+                }
1192
+
1193
+                if (!$unlockLater
1194
+                    && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1195
+                ) {
1196
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1197
+                }
1198
+                return $result;
1199
+            } else {
1200
+                $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1201
+            }
1202
+        }
1203
+        return null;
1204
+    }
1205
+
1206
+    /**
1207
+     * get the path relative to the default root for hook usage
1208
+     *
1209
+     * @param string $path
1210
+     * @return string
1211
+     */
1212
+    private function getHookPath($path) {
1213
+        if (!Filesystem::getView()) {
1214
+            return $path;
1215
+        }
1216
+        return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1217
+    }
1218
+
1219
+    private function shouldEmitHooks($path = '') {
1220
+        if ($path && Cache\Scanner::isPartialFile($path)) {
1221
+            return false;
1222
+        }
1223
+        if (!Filesystem::$loaded) {
1224
+            return false;
1225
+        }
1226
+        $defaultRoot = Filesystem::getRoot();
1227
+        if ($defaultRoot === null) {
1228
+            return false;
1229
+        }
1230
+        if ($this->fakeRoot === $defaultRoot) {
1231
+            return true;
1232
+        }
1233
+        $fullPath = $this->getAbsolutePath($path);
1234
+
1235
+        if ($fullPath === $defaultRoot) {
1236
+            return true;
1237
+        }
1238
+
1239
+        return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1240
+    }
1241
+
1242
+    /**
1243
+     * @param string[] $hooks
1244
+     * @param string $path
1245
+     * @param bool $post
1246
+     * @return bool
1247
+     */
1248
+    private function runHooks($hooks, $path, $post = false) {
1249
+        $relativePath = $path;
1250
+        $path = $this->getHookPath($path);
1251
+        $prefix = ($post) ? 'post_' : '';
1252
+        $run = true;
1253
+        if ($this->shouldEmitHooks($relativePath)) {
1254
+            foreach ($hooks as $hook) {
1255
+                if ($hook != 'read') {
1256
+                    \OC_Hook::emit(
1257
+                        Filesystem::CLASSNAME,
1258
+                        $prefix . $hook,
1259
+                        array(
1260
+                            Filesystem::signal_param_run => &$run,
1261
+                            Filesystem::signal_param_path => $path
1262
+                        )
1263
+                    );
1264
+                } elseif (!$post) {
1265
+                    \OC_Hook::emit(
1266
+                        Filesystem::CLASSNAME,
1267
+                        $prefix . $hook,
1268
+                        array(
1269
+                            Filesystem::signal_param_path => $path
1270
+                        )
1271
+                    );
1272
+                }
1273
+            }
1274
+        }
1275
+        return $run;
1276
+    }
1277
+
1278
+    /**
1279
+     * check if a file or folder has been updated since $time
1280
+     *
1281
+     * @param string $path
1282
+     * @param int $time
1283
+     * @return bool
1284
+     */
1285
+    public function hasUpdated($path, $time) {
1286
+        return $this->basicOperation('hasUpdated', $path, array(), $time);
1287
+    }
1288
+
1289
+    /**
1290
+     * @param string $ownerId
1291
+     * @return \OC\User\User
1292
+     */
1293
+    private function getUserObjectForOwner($ownerId) {
1294
+        $owner = $this->userManager->get($ownerId);
1295
+        if ($owner instanceof IUser) {
1296
+            return $owner;
1297
+        } else {
1298
+            return new User($ownerId, null);
1299
+        }
1300
+    }
1301
+
1302
+    /**
1303
+     * Get file info from cache
1304
+     *
1305
+     * If the file is not in cached it will be scanned
1306
+     * If the file has changed on storage the cache will be updated
1307
+     *
1308
+     * @param \OC\Files\Storage\Storage $storage
1309
+     * @param string $internalPath
1310
+     * @param string $relativePath
1311
+     * @return ICacheEntry|bool
1312
+     */
1313
+    private function getCacheEntry($storage, $internalPath, $relativePath) {
1314
+        $cache = $storage->getCache($internalPath);
1315
+        $data = $cache->get($internalPath);
1316
+        $watcher = $storage->getWatcher($internalPath);
1317
+
1318
+        try {
1319
+            // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1320
+            if (!$data || $data['size'] === -1) {
1321
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1322
+                if (!$storage->file_exists($internalPath)) {
1323
+                    $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1324
+                    return false;
1325
+                }
1326
+                $scanner = $storage->getScanner($internalPath);
1327
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1328
+                $data = $cache->get($internalPath);
1329
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1330
+            } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1331
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1332
+                $watcher->update($internalPath, $data);
1333
+                $storage->getPropagator()->propagateChange($internalPath, time());
1334
+                $data = $cache->get($internalPath);
1335
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1336
+            }
1337
+        } catch (LockedException $e) {
1338
+            // if the file is locked we just use the old cache info
1339
+        }
1340
+
1341
+        return $data;
1342
+    }
1343
+
1344
+    /**
1345
+     * get the filesystem info
1346
+     *
1347
+     * @param string $path
1348
+     * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1349
+     * 'ext' to add only ext storage mount point sizes. Defaults to true.
1350
+     * defaults to true
1351
+     * @return \OC\Files\FileInfo|false False if file does not exist
1352
+     */
1353
+    public function getFileInfo($path, $includeMountPoints = true) {
1354
+        $this->assertPathLength($path);
1355
+        if (!Filesystem::isValidPath($path)) {
1356
+            return false;
1357
+        }
1358
+        if (Cache\Scanner::isPartialFile($path)) {
1359
+            return $this->getPartFileInfo($path);
1360
+        }
1361
+        $relativePath = $path;
1362
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1363
+
1364
+        $mount = Filesystem::getMountManager()->find($path);
1365
+        if (!$mount) {
1366
+            return false;
1367
+        }
1368
+        $storage = $mount->getStorage();
1369
+        $internalPath = $mount->getInternalPath($path);
1370
+        if ($storage) {
1371
+            $data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1372
+
1373
+            if (!$data instanceof ICacheEntry) {
1374
+                return false;
1375
+            }
1376
+
1377
+            if ($mount instanceof MoveableMount && $internalPath === '') {
1378
+                $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1379
+            }
1380
+
1381
+            $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1382
+            $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1383
+
1384
+            if ($data and isset($data['fileid'])) {
1385
+                if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1386
+                    //add the sizes of other mount points to the folder
1387
+                    $extOnly = ($includeMountPoints === 'ext');
1388
+                    $mounts = Filesystem::getMountManager()->findIn($path);
1389
+                    $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1390
+                        $subStorage = $mount->getStorage();
1391
+                        return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392
+                    }));
1393
+                }
1394
+            }
1395
+
1396
+            return $info;
1397
+        }
1398
+
1399
+        return false;
1400
+    }
1401
+
1402
+    /**
1403
+     * get the content of a directory
1404
+     *
1405
+     * @param string $directory path under datadirectory
1406
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1407
+     * @return FileInfo[]
1408
+     */
1409
+    public function getDirectoryContent($directory, $mimetype_filter = '') {
1410
+        $this->assertPathLength($directory);
1411
+        if (!Filesystem::isValidPath($directory)) {
1412
+            return [];
1413
+        }
1414
+        $path = $this->getAbsolutePath($directory);
1415
+        $path = Filesystem::normalizePath($path);
1416
+        $mount = $this->getMount($directory);
1417
+        if (!$mount) {
1418
+            return [];
1419
+        }
1420
+        $storage = $mount->getStorage();
1421
+        $internalPath = $mount->getInternalPath($path);
1422
+        if ($storage) {
1423
+            $cache = $storage->getCache($internalPath);
1424
+            $user = \OC_User::getUser();
1425
+
1426
+            $data = $this->getCacheEntry($storage, $internalPath, $directory);
1427
+
1428
+            if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1429
+                return [];
1430
+            }
1431
+
1432
+            $folderId = $data['fileid'];
1433
+            $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1434
+
1435
+            $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1436
+            /**
1437
+             * @var \OC\Files\FileInfo[] $files
1438
+             */
1439
+            $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440
+                if ($sharingDisabled) {
1441
+                    $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442
+                }
1443
+                $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
+                return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1445
+            }, $contents);
1446
+
1447
+            //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1448
+            $mounts = Filesystem::getMountManager()->findIn($path);
1449
+            $dirLength = strlen($path);
1450
+            foreach ($mounts as $mount) {
1451
+                $mountPoint = $mount->getMountPoint();
1452
+                $subStorage = $mount->getStorage();
1453
+                if ($subStorage) {
1454
+                    $subCache = $subStorage->getCache('');
1455
+
1456
+                    $rootEntry = $subCache->get('');
1457
+                    if (!$rootEntry) {
1458
+                        $subScanner = $subStorage->getScanner('');
1459
+                        try {
1460
+                            $subScanner->scanFile('');
1461
+                        } catch (\OCP\Files\StorageNotAvailableException $e) {
1462
+                            continue;
1463
+                        } catch (\OCP\Files\StorageInvalidException $e) {
1464
+                            continue;
1465
+                        } catch (\Exception $e) {
1466
+                            // sometimes when the storage is not available it can be any exception
1467
+                            \OC::$server->getLogger()->logException($e, [
1468
+                                'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1469
+                                'level' => \OCP\Util::ERROR,
1470
+                                'app' => 'lib',
1471
+                            ]);
1472
+                            continue;
1473
+                        }
1474
+                        $rootEntry = $subCache->get('');
1475
+                    }
1476
+
1477
+                    if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1478
+                        $relativePath = trim(substr($mountPoint, $dirLength), '/');
1479
+                        if ($pos = strpos($relativePath, '/')) {
1480
+                            //mountpoint inside subfolder add size to the correct folder
1481
+                            $entryName = substr($relativePath, 0, $pos);
1482
+                            foreach ($files as &$entry) {
1483
+                                if ($entry->getName() === $entryName) {
1484
+                                    $entry->addSubEntry($rootEntry, $mountPoint);
1485
+                                }
1486
+                            }
1487
+                        } else { //mountpoint in this folder, add an entry for it
1488
+                            $rootEntry['name'] = $relativePath;
1489
+                            $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1490
+                            $permissions = $rootEntry['permissions'];
1491
+                            // do not allow renaming/deleting the mount point if they are not shared files/folders
1492
+                            // for shared files/folders we use the permissions given by the owner
1493
+                            if ($mount instanceof MoveableMount) {
1494
+                                $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1495
+                            } else {
1496
+                                $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1497
+                            }
1498
+
1499
+                            //remove any existing entry with the same name
1500
+                            foreach ($files as $i => $file) {
1501
+                                if ($file['name'] === $rootEntry['name']) {
1502
+                                    unset($files[$i]);
1503
+                                    break;
1504
+                                }
1505
+                            }
1506
+                            $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507
+
1508
+                            // if sharing was disabled for the user we remove the share permissions
1509
+                            if (\OCP\Util::isSharingDisabledForUser()) {
1510
+                                $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1511
+                            }
1512
+
1513
+                            $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
+                            $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515
+                        }
1516
+                    }
1517
+                }
1518
+            }
1519
+
1520
+            if ($mimetype_filter) {
1521
+                $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1522
+                    if (strpos($mimetype_filter, '/')) {
1523
+                        return $file->getMimetype() === $mimetype_filter;
1524
+                    } else {
1525
+                        return $file->getMimePart() === $mimetype_filter;
1526
+                    }
1527
+                });
1528
+            }
1529
+
1530
+            return $files;
1531
+        } else {
1532
+            return [];
1533
+        }
1534
+    }
1535
+
1536
+    /**
1537
+     * change file metadata
1538
+     *
1539
+     * @param string $path
1540
+     * @param array|\OCP\Files\FileInfo $data
1541
+     * @return int
1542
+     *
1543
+     * returns the fileid of the updated file
1544
+     */
1545
+    public function putFileInfo($path, $data) {
1546
+        $this->assertPathLength($path);
1547
+        if ($data instanceof FileInfo) {
1548
+            $data = $data->getData();
1549
+        }
1550
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1551
+        /**
1552
+         * @var \OC\Files\Storage\Storage $storage
1553
+         * @var string $internalPath
1554
+         */
1555
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
1556
+        if ($storage) {
1557
+            $cache = $storage->getCache($path);
1558
+
1559
+            if (!$cache->inCache($internalPath)) {
1560
+                $scanner = $storage->getScanner($internalPath);
1561
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1562
+            }
1563
+
1564
+            return $cache->put($internalPath, $data);
1565
+        } else {
1566
+            return -1;
1567
+        }
1568
+    }
1569
+
1570
+    /**
1571
+     * search for files with the name matching $query
1572
+     *
1573
+     * @param string $query
1574
+     * @return FileInfo[]
1575
+     */
1576
+    public function search($query) {
1577
+        return $this->searchCommon('search', array('%' . $query . '%'));
1578
+    }
1579
+
1580
+    /**
1581
+     * search for files with the name matching $query
1582
+     *
1583
+     * @param string $query
1584
+     * @return FileInfo[]
1585
+     */
1586
+    public function searchRaw($query) {
1587
+        return $this->searchCommon('search', array($query));
1588
+    }
1589
+
1590
+    /**
1591
+     * search for files by mimetype
1592
+     *
1593
+     * @param string $mimetype
1594
+     * @return FileInfo[]
1595
+     */
1596
+    public function searchByMime($mimetype) {
1597
+        return $this->searchCommon('searchByMime', array($mimetype));
1598
+    }
1599
+
1600
+    /**
1601
+     * search for files by tag
1602
+     *
1603
+     * @param string|int $tag name or tag id
1604
+     * @param string $userId owner of the tags
1605
+     * @return FileInfo[]
1606
+     */
1607
+    public function searchByTag($tag, $userId) {
1608
+        return $this->searchCommon('searchByTag', array($tag, $userId));
1609
+    }
1610
+
1611
+    /**
1612
+     * @param string $method cache method
1613
+     * @param array $args
1614
+     * @return FileInfo[]
1615
+     */
1616
+    private function searchCommon($method, $args) {
1617
+        $files = array();
1618
+        $rootLength = strlen($this->fakeRoot);
1619
+
1620
+        $mount = $this->getMount('');
1621
+        $mountPoint = $mount->getMountPoint();
1622
+        $storage = $mount->getStorage();
1623
+        if ($storage) {
1624
+            $cache = $storage->getCache('');
1625
+
1626
+            $results = call_user_func_array(array($cache, $method), $args);
1627
+            foreach ($results as $result) {
1628
+                if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1629
+                    $internalPath = $result['path'];
1630
+                    $path = $mountPoint . $result['path'];
1631
+                    $result['path'] = substr($mountPoint . $result['path'], $rootLength);
1632
+                    $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633
+                    $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634
+                }
1635
+            }
1636
+
1637
+            $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1638
+            foreach ($mounts as $mount) {
1639
+                $mountPoint = $mount->getMountPoint();
1640
+                $storage = $mount->getStorage();
1641
+                if ($storage) {
1642
+                    $cache = $storage->getCache('');
1643
+
1644
+                    $relativeMountPoint = substr($mountPoint, $rootLength);
1645
+                    $results = call_user_func_array(array($cache, $method), $args);
1646
+                    if ($results) {
1647
+                        foreach ($results as $result) {
1648
+                            $internalPath = $result['path'];
1649
+                            $result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
+                            $path = rtrim($mountPoint . $internalPath, '/');
1651
+                            $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652
+                            $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653
+                        }
1654
+                    }
1655
+                }
1656
+            }
1657
+        }
1658
+        return $files;
1659
+    }
1660
+
1661
+    /**
1662
+     * Get the owner for a file or folder
1663
+     *
1664
+     * @param string $path
1665
+     * @return string the user id of the owner
1666
+     * @throws NotFoundException
1667
+     */
1668
+    public function getOwner($path) {
1669
+        $info = $this->getFileInfo($path);
1670
+        if (!$info) {
1671
+            throw new NotFoundException($path . ' not found while trying to get owner');
1672
+        }
1673
+        return $info->getOwner()->getUID();
1674
+    }
1675
+
1676
+    /**
1677
+     * get the ETag for a file or folder
1678
+     *
1679
+     * @param string $path
1680
+     * @return string
1681
+     */
1682
+    public function getETag($path) {
1683
+        /**
1684
+         * @var Storage\Storage $storage
1685
+         * @var string $internalPath
1686
+         */
1687
+        list($storage, $internalPath) = $this->resolvePath($path);
1688
+        if ($storage) {
1689
+            return $storage->getETag($internalPath);
1690
+        } else {
1691
+            return null;
1692
+        }
1693
+    }
1694
+
1695
+    /**
1696
+     * Get the path of a file by id, relative to the view
1697
+     *
1698
+     * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1699
+     *
1700
+     * @param int $id
1701
+     * @throws NotFoundException
1702
+     * @return string
1703
+     */
1704
+    public function getPath($id) {
1705
+        $id = (int)$id;
1706
+        $manager = Filesystem::getMountManager();
1707
+        $mounts = $manager->findIn($this->fakeRoot);
1708
+        $mounts[] = $manager->find($this->fakeRoot);
1709
+        // reverse the array so we start with the storage this view is in
1710
+        // which is the most likely to contain the file we're looking for
1711
+        $mounts = array_reverse($mounts);
1712
+        foreach ($mounts as $mount) {
1713
+            /**
1714
+             * @var \OC\Files\Mount\MountPoint $mount
1715
+             */
1716
+            if ($mount->getStorage()) {
1717
+                $cache = $mount->getStorage()->getCache();
1718
+                $internalPath = $cache->getPathById($id);
1719
+                if (is_string($internalPath)) {
1720
+                    $fullPath = $mount->getMountPoint() . $internalPath;
1721
+                    if (!is_null($path = $this->getRelativePath($fullPath))) {
1722
+                        return $path;
1723
+                    }
1724
+                }
1725
+            }
1726
+        }
1727
+        throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1728
+    }
1729
+
1730
+    /**
1731
+     * @param string $path
1732
+     * @throws InvalidPathException
1733
+     */
1734
+    private function assertPathLength($path) {
1735
+        $maxLen = min(PHP_MAXPATHLEN, 4000);
1736
+        // Check for the string length - performed using isset() instead of strlen()
1737
+        // because isset() is about 5x-40x faster.
1738
+        if (isset($path[$maxLen])) {
1739
+            $pathLen = strlen($path);
1740
+            throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1741
+        }
1742
+    }
1743
+
1744
+    /**
1745
+     * check if it is allowed to move a mount point to a given target.
1746
+     * It is not allowed to move a mount point into a different mount point or
1747
+     * into an already shared folder
1748
+     *
1749
+     * @param string $target path
1750
+     * @return boolean
1751
+     */
1752
+    private function isTargetAllowed($target) {
1753
+
1754
+        list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1755
+        if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1756
+            \OCP\Util::writeLog('files',
1757
+                'It is not allowed to move one mount point into another one',
1758
+                \OCP\Util::DEBUG);
1759
+            return false;
1760
+        }
1761
+
1762
+        // note: cannot use the view because the target is already locked
1763
+        $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1764
+        if ($fileId === -1) {
1765
+            // target might not exist, need to check parent instead
1766
+            $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1767
+        }
1768
+
1769
+        // check if any of the parents were shared by the current owner (include collections)
1770
+        $shares = \OCP\Share::getItemShared(
1771
+            'folder',
1772
+            $fileId,
1773
+            \OCP\Share::FORMAT_NONE,
1774
+            null,
1775
+            true
1776
+        );
1777
+
1778
+        if (count($shares) > 0) {
1779
+            \OCP\Util::writeLog('files',
1780
+                'It is not allowed to move one mount point into a shared folder',
1781
+                \OCP\Util::DEBUG);
1782
+            return false;
1783
+        }
1784
+
1785
+        return true;
1786
+    }
1787
+
1788
+    /**
1789
+     * Get a fileinfo object for files that are ignored in the cache (part files)
1790
+     *
1791
+     * @param string $path
1792
+     * @return \OCP\Files\FileInfo
1793
+     */
1794
+    private function getPartFileInfo($path) {
1795
+        $mount = $this->getMount($path);
1796
+        $storage = $mount->getStorage();
1797
+        $internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1798
+        $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1799
+        return new FileInfo(
1800
+            $this->getAbsolutePath($path),
1801
+            $storage,
1802
+            $internalPath,
1803
+            [
1804
+                'fileid' => null,
1805
+                'mimetype' => $storage->getMimeType($internalPath),
1806
+                'name' => basename($path),
1807
+                'etag' => null,
1808
+                'size' => $storage->filesize($internalPath),
1809
+                'mtime' => $storage->filemtime($internalPath),
1810
+                'encrypted' => false,
1811
+                'permissions' => \OCP\Constants::PERMISSION_ALL
1812
+            ],
1813
+            $mount,
1814
+            $owner
1815
+        );
1816
+    }
1817
+
1818
+    /**
1819
+     * @param string $path
1820
+     * @param string $fileName
1821
+     * @throws InvalidPathException
1822
+     */
1823
+    public function verifyPath($path, $fileName) {
1824
+        try {
1825
+            /** @type \OCP\Files\Storage $storage */
1826
+            list($storage, $internalPath) = $this->resolvePath($path);
1827
+            $storage->verifyPath($internalPath, $fileName);
1828
+        } catch (ReservedWordException $ex) {
1829
+            $l = \OC::$server->getL10N('lib');
1830
+            throw new InvalidPathException($l->t('File name is a reserved word'));
1831
+        } catch (InvalidCharacterInPathException $ex) {
1832
+            $l = \OC::$server->getL10N('lib');
1833
+            throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1834
+        } catch (FileNameTooLongException $ex) {
1835
+            $l = \OC::$server->getL10N('lib');
1836
+            throw new InvalidPathException($l->t('File name is too long'));
1837
+        } catch (InvalidDirectoryException $ex) {
1838
+            $l = \OC::$server->getL10N('lib');
1839
+            throw new InvalidPathException($l->t('Dot files are not allowed'));
1840
+        } catch (EmptyFileNameException $ex) {
1841
+            $l = \OC::$server->getL10N('lib');
1842
+            throw new InvalidPathException($l->t('Empty filename is not allowed'));
1843
+        }
1844
+    }
1845
+
1846
+    /**
1847
+     * get all parent folders of $path
1848
+     *
1849
+     * @param string $path
1850
+     * @return string[]
1851
+     */
1852
+    private function getParents($path) {
1853
+        $path = trim($path, '/');
1854
+        if (!$path) {
1855
+            return [];
1856
+        }
1857
+
1858
+        $parts = explode('/', $path);
1859
+
1860
+        // remove the single file
1861
+        array_pop($parts);
1862
+        $result = array('/');
1863
+        $resultPath = '';
1864
+        foreach ($parts as $part) {
1865
+            if ($part) {
1866
+                $resultPath .= '/' . $part;
1867
+                $result[] = $resultPath;
1868
+            }
1869
+        }
1870
+        return $result;
1871
+    }
1872
+
1873
+    /**
1874
+     * Returns the mount point for which to lock
1875
+     *
1876
+     * @param string $absolutePath absolute path
1877
+     * @param bool $useParentMount true to return parent mount instead of whatever
1878
+     * is mounted directly on the given path, false otherwise
1879
+     * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1880
+     */
1881
+    private function getMountForLock($absolutePath, $useParentMount = false) {
1882
+        $results = [];
1883
+        $mount = Filesystem::getMountManager()->find($absolutePath);
1884
+        if (!$mount) {
1885
+            return $results;
1886
+        }
1887
+
1888
+        if ($useParentMount) {
1889
+            // find out if something is mounted directly on the path
1890
+            $internalPath = $mount->getInternalPath($absolutePath);
1891
+            if ($internalPath === '') {
1892
+                // resolve the parent mount instead
1893
+                $mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1894
+            }
1895
+        }
1896
+
1897
+        return $mount;
1898
+    }
1899
+
1900
+    /**
1901
+     * Lock the given path
1902
+     *
1903
+     * @param string $path the path of the file to lock, relative to the view
1904
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1905
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1906
+     *
1907
+     * @return bool False if the path is excluded from locking, true otherwise
1908
+     * @throws \OCP\Lock\LockedException if the path is already locked
1909
+     */
1910
+    private function lockPath($path, $type, $lockMountPoint = false) {
1911
+        $absolutePath = $this->getAbsolutePath($path);
1912
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1913
+        if (!$this->shouldLockFile($absolutePath)) {
1914
+            return false;
1915
+        }
1916
+
1917
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1918
+        if ($mount) {
1919
+            try {
1920
+                $storage = $mount->getStorage();
1921
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1922
+                    $storage->acquireLock(
1923
+                        $mount->getInternalPath($absolutePath),
1924
+                        $type,
1925
+                        $this->lockingProvider
1926
+                    );
1927
+                }
1928
+            } catch (\OCP\Lock\LockedException $e) {
1929
+                // rethrow with the a human-readable path
1930
+                throw new \OCP\Lock\LockedException(
1931
+                    $this->getPathRelativeToFiles($absolutePath),
1932
+                    $e
1933
+                );
1934
+            }
1935
+        }
1936
+
1937
+        return true;
1938
+    }
1939
+
1940
+    /**
1941
+     * Change the lock type
1942
+     *
1943
+     * @param string $path the path of the file to lock, relative to the view
1944
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1945
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1946
+     *
1947
+     * @return bool False if the path is excluded from locking, true otherwise
1948
+     * @throws \OCP\Lock\LockedException if the path is already locked
1949
+     */
1950
+    public function changeLock($path, $type, $lockMountPoint = false) {
1951
+        $path = Filesystem::normalizePath($path);
1952
+        $absolutePath = $this->getAbsolutePath($path);
1953
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1954
+        if (!$this->shouldLockFile($absolutePath)) {
1955
+            return false;
1956
+        }
1957
+
1958
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1959
+        if ($mount) {
1960
+            try {
1961
+                $storage = $mount->getStorage();
1962
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1963
+                    $storage->changeLock(
1964
+                        $mount->getInternalPath($absolutePath),
1965
+                        $type,
1966
+                        $this->lockingProvider
1967
+                    );
1968
+                }
1969
+            } catch (\OCP\Lock\LockedException $e) {
1970
+                try {
1971
+                    // rethrow with the a human-readable path
1972
+                    throw new \OCP\Lock\LockedException(
1973
+                        $this->getPathRelativeToFiles($absolutePath),
1974
+                        $e
1975
+                    );
1976
+                } catch (\InvalidArgumentException $e) {
1977
+                    throw new \OCP\Lock\LockedException(
1978
+                        $absolutePath,
1979
+                        $e
1980
+                    );
1981
+                }
1982
+            }
1983
+        }
1984
+
1985
+        return true;
1986
+    }
1987
+
1988
+    /**
1989
+     * Unlock the given path
1990
+     *
1991
+     * @param string $path the path of the file to unlock, relative to the view
1992
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1993
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1994
+     *
1995
+     * @return bool False if the path is excluded from locking, true otherwise
1996
+     */
1997
+    private function unlockPath($path, $type, $lockMountPoint = false) {
1998
+        $absolutePath = $this->getAbsolutePath($path);
1999
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2000
+        if (!$this->shouldLockFile($absolutePath)) {
2001
+            return false;
2002
+        }
2003
+
2004
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2005
+        if ($mount) {
2006
+            $storage = $mount->getStorage();
2007
+            if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2008
+                $storage->releaseLock(
2009
+                    $mount->getInternalPath($absolutePath),
2010
+                    $type,
2011
+                    $this->lockingProvider
2012
+                );
2013
+            }
2014
+        }
2015
+
2016
+        return true;
2017
+    }
2018
+
2019
+    /**
2020
+     * Lock a path and all its parents up to the root of the view
2021
+     *
2022
+     * @param string $path the path of the file to lock relative to the view
2023
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2024
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2025
+     *
2026
+     * @return bool False if the path is excluded from locking, true otherwise
2027
+     */
2028
+    public function lockFile($path, $type, $lockMountPoint = false) {
2029
+        $absolutePath = $this->getAbsolutePath($path);
2030
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2031
+        if (!$this->shouldLockFile($absolutePath)) {
2032
+            return false;
2033
+        }
2034
+
2035
+        $this->lockPath($path, $type, $lockMountPoint);
2036
+
2037
+        $parents = $this->getParents($path);
2038
+        foreach ($parents as $parent) {
2039
+            $this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2040
+        }
2041
+
2042
+        return true;
2043
+    }
2044
+
2045
+    /**
2046
+     * Unlock a path and all its parents up to the root of the view
2047
+     *
2048
+     * @param string $path the path of the file to lock relative to the view
2049
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2050
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2051
+     *
2052
+     * @return bool False if the path is excluded from locking, true otherwise
2053
+     */
2054
+    public function unlockFile($path, $type, $lockMountPoint = false) {
2055
+        $absolutePath = $this->getAbsolutePath($path);
2056
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2057
+        if (!$this->shouldLockFile($absolutePath)) {
2058
+            return false;
2059
+        }
2060
+
2061
+        $this->unlockPath($path, $type, $lockMountPoint);
2062
+
2063
+        $parents = $this->getParents($path);
2064
+        foreach ($parents as $parent) {
2065
+            $this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2066
+        }
2067
+
2068
+        return true;
2069
+    }
2070
+
2071
+    /**
2072
+     * Only lock files in data/user/files/
2073
+     *
2074
+     * @param string $path Absolute path to the file/folder we try to (un)lock
2075
+     * @return bool
2076
+     */
2077
+    protected function shouldLockFile($path) {
2078
+        $path = Filesystem::normalizePath($path);
2079
+
2080
+        $pathSegments = explode('/', $path);
2081
+        if (isset($pathSegments[2])) {
2082
+            // E.g.: /username/files/path-to-file
2083
+            return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2084
+        }
2085
+
2086
+        return strpos($path, '/appdata_') !== 0;
2087
+    }
2088
+
2089
+    /**
2090
+     * Shortens the given absolute path to be relative to
2091
+     * "$user/files".
2092
+     *
2093
+     * @param string $absolutePath absolute path which is under "files"
2094
+     *
2095
+     * @return string path relative to "files" with trimmed slashes or null
2096
+     * if the path was NOT relative to files
2097
+     *
2098
+     * @throws \InvalidArgumentException if the given path was not under "files"
2099
+     * @since 8.1.0
2100
+     */
2101
+    public function getPathRelativeToFiles($absolutePath) {
2102
+        $path = Filesystem::normalizePath($absolutePath);
2103
+        $parts = explode('/', trim($path, '/'), 3);
2104
+        // "$user", "files", "path/to/dir"
2105
+        if (!isset($parts[1]) || $parts[1] !== 'files') {
2106
+            $this->logger->error(
2107
+                '$absolutePath must be relative to "files", value is "%s"',
2108
+                [
2109
+                    $absolutePath
2110
+                ]
2111
+            );
2112
+            throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2113
+        }
2114
+        if (isset($parts[2])) {
2115
+            return $parts[2];
2116
+        }
2117
+        return '';
2118
+    }
2119
+
2120
+    /**
2121
+     * @param string $filename
2122
+     * @return array
2123
+     * @throws \OC\User\NoUserException
2124
+     * @throws NotFoundException
2125
+     */
2126
+    public function getUidAndFilename($filename) {
2127
+        $info = $this->getFileInfo($filename);
2128
+        if (!$info instanceof \OCP\Files\FileInfo) {
2129
+            throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2130
+        }
2131
+        $uid = $info->getOwner()->getUID();
2132
+        if ($uid != \OCP\User::getUser()) {
2133
+            Filesystem::initMountPoints($uid);
2134
+            $ownerView = new View('/' . $uid . '/files');
2135
+            try {
2136
+                $filename = $ownerView->getPath($info['fileid']);
2137
+            } catch (NotFoundException $e) {
2138
+                throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2139
+            }
2140
+        }
2141
+        return [$uid, $filename];
2142
+    }
2143
+
2144
+    /**
2145
+     * Creates parent non-existing folders
2146
+     *
2147
+     * @param string $filePath
2148
+     * @return bool
2149
+     */
2150
+    private function createParentDirectories($filePath) {
2151
+        $directoryParts = explode('/', $filePath);
2152
+        $directoryParts = array_filter($directoryParts);
2153
+        foreach ($directoryParts as $key => $part) {
2154
+            $currentPathElements = array_slice($directoryParts, 0, $key);
2155
+            $currentPath = '/' . implode('/', $currentPathElements);
2156
+            if ($this->is_file($currentPath)) {
2157
+                return false;
2158
+            }
2159
+            if (!$this->file_exists($currentPath)) {
2160
+                $this->mkdir($currentPath);
2161
+            }
2162
+        }
2163
+
2164
+        return true;
2165
+    }
2166 2166
 }
Please login to merge, or discard this patch.
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -126,9 +126,9 @@  discard block
 block discarded – undo
126 126
 			$path = '/';
127 127
 		}
128 128
 		if ($path[0] !== '/') {
129
-			$path = '/' . $path;
129
+			$path = '/'.$path;
130 130
 		}
131
-		return $this->fakeRoot . $path;
131
+		return $this->fakeRoot.$path;
132 132
 	}
133 133
 
134 134
 	/**
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 	public function chroot($fakeRoot) {
141 141
 		if (!$fakeRoot == '') {
142 142
 			if ($fakeRoot[0] !== '/') {
143
-				$fakeRoot = '/' . $fakeRoot;
143
+				$fakeRoot = '/'.$fakeRoot;
144 144
 			}
145 145
 		}
146 146
 		$this->fakeRoot = $fakeRoot;
@@ -172,7 +172,7 @@  discard block
 block discarded – undo
172 172
 		}
173 173
 
174 174
 		// missing slashes can cause wrong matches!
175
-		$root = rtrim($this->fakeRoot, '/') . '/';
175
+		$root = rtrim($this->fakeRoot, '/').'/';
176 176
 
177 177
 		if (strpos($path, $root) !== 0) {
178 178
 			return null;
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
 		if ($mount instanceof MoveableMount) {
279 279
 			// cut of /user/files to get the relative path to data/user/files
280 280
 			$pathParts = explode('/', $path, 4);
281
-			$relPath = '/' . $pathParts[3];
281
+			$relPath = '/'.$pathParts[3];
282 282
 			$this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283 283
 			\OC_Hook::emit(
284 284
 				Filesystem::CLASSNAME, "umount",
@@ -700,7 +700,7 @@  discard block
 block discarded – undo
700 700
 		}
701 701
 		$postFix = (substr($path, -1, 1) === '/') ? '/' : '';
702 702
 		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
703
+		$mount = Filesystem::getMountManager()->find($absolutePath.$postFix);
704 704
 		if ($mount and $mount->getInternalPath($absolutePath) === '') {
705 705
 			return $this->removeMount($mount, $absolutePath);
706 706
 		}
@@ -820,7 +820,7 @@  discard block
 block discarded – undo
820 820
 								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821 821
 							}
822 822
 						}
823
-					} catch(\Exception $e) {
823
+					} catch (\Exception $e) {
824 824
 						throw $e;
825 825
 					} finally {
826 826
 						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
@@ -844,7 +844,7 @@  discard block
 block discarded – undo
844 844
 						}
845 845
 					}
846 846
 				}
847
-			} catch(\Exception $e) {
847
+			} catch (\Exception $e) {
848 848
 				throw $e;
849 849
 			} finally {
850 850
 				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
@@ -977,7 +977,7 @@  discard block
 block discarded – undo
977 977
 				$hooks[] = 'write';
978 978
 				break;
979 979
 			default:
980
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
980
+				\OCP\Util::writeLog('core', 'invalid mode ('.$mode.') for '.$path, \OCP\Util::ERROR);
981 981
 		}
982 982
 
983 983
 		if ($mode !== 'r' && $mode !== 'w') {
@@ -1081,7 +1081,7 @@  discard block
 block discarded – undo
1081 1081
 					array(Filesystem::signal_param_path => $this->getHookPath($path))
1082 1082
 				);
1083 1083
 			}
1084
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1084
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1085 1085
 			if ($storage) {
1086 1086
 				$result = $storage->hash($type, $internalPath, $raw);
1087 1087
 				return $result;
@@ -1136,7 +1136,7 @@  discard block
 block discarded – undo
1136 1136
 
1137 1137
 			$run = $this->runHooks($hooks, $path);
1138 1138
 			/** @var \OC\Files\Storage\Storage $storage */
1139
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1139
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1140 1140
 			if ($run and $storage) {
1141 1141
 				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142 1142
 					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
@@ -1175,7 +1175,7 @@  discard block
 block discarded – undo
1175 1175
 					$unlockLater = true;
1176 1176
 					// make sure our unlocking callback will still be called if connection is aborted
1177 1177
 					ignore_user_abort(true);
1178
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1178
+					$result = CallbackWrapper::wrap($result, null, null, function() use ($hooks, $path) {
1179 1179
 						if (in_array('write', $hooks)) {
1180 1180
 							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181 1181
 						} else if (in_array('read', $hooks)) {
@@ -1236,7 +1236,7 @@  discard block
 block discarded – undo
1236 1236
 			return true;
1237 1237
 		}
1238 1238
 
1239
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1239
+		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot.'/');
1240 1240
 	}
1241 1241
 
1242 1242
 	/**
@@ -1255,7 +1255,7 @@  discard block
 block discarded – undo
1255 1255
 				if ($hook != 'read') {
1256 1256
 					\OC_Hook::emit(
1257 1257
 						Filesystem::CLASSNAME,
1258
-						$prefix . $hook,
1258
+						$prefix.$hook,
1259 1259
 						array(
1260 1260
 							Filesystem::signal_param_run => &$run,
1261 1261
 							Filesystem::signal_param_path => $path
@@ -1264,7 +1264,7 @@  discard block
 block discarded – undo
1264 1264
 				} elseif (!$post) {
1265 1265
 					\OC_Hook::emit(
1266 1266
 						Filesystem::CLASSNAME,
1267
-						$prefix . $hook,
1267
+						$prefix.$hook,
1268 1268
 						array(
1269 1269
 							Filesystem::signal_param_path => $path
1270 1270
 						)
@@ -1359,7 +1359,7 @@  discard block
 block discarded – undo
1359 1359
 			return $this->getPartFileInfo($path);
1360 1360
 		}
1361 1361
 		$relativePath = $path;
1362
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1362
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1363 1363
 
1364 1364
 		$mount = Filesystem::getMountManager()->find($path);
1365 1365
 		if (!$mount) {
@@ -1386,7 +1386,7 @@  discard block
 block discarded – undo
1386 1386
 					//add the sizes of other mount points to the folder
1387 1387
 					$extOnly = ($includeMountPoints === 'ext');
1388 1388
 					$mounts = Filesystem::getMountManager()->findIn($path);
1389
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1389
+					$info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) {
1390 1390
 						$subStorage = $mount->getStorage();
1391 1391
 						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392 1392
 					}));
@@ -1436,12 +1436,12 @@  discard block
 block discarded – undo
1436 1436
 			/**
1437 1437
 			 * @var \OC\Files\FileInfo[] $files
1438 1438
 			 */
1439
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1439
+			$files = array_map(function(ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440 1440
 				if ($sharingDisabled) {
1441 1441
 					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442 1442
 				}
1443 1443
 				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1444
+				return new FileInfo($path.'/'.$content['name'], $storage, $content['path'], $content, $mount, $owner);
1445 1445
 			}, $contents);
1446 1446
 
1447 1447
 			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
@@ -1465,7 +1465,7 @@  discard block
 block discarded – undo
1465 1465
 						} catch (\Exception $e) {
1466 1466
 							// sometimes when the storage is not available it can be any exception
1467 1467
 							\OC::$server->getLogger()->logException($e, [
1468
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1468
+								'message' => 'Exception while scanning storage "'.$subStorage->getId().'"',
1469 1469
 								'level' => \OCP\Util::ERROR,
1470 1470
 								'app' => 'lib',
1471 1471
 							]);
@@ -1503,7 +1503,7 @@  discard block
 block discarded – undo
1503 1503
 									break;
1504 1504
 								}
1505 1505
 							}
1506
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1506
+							$rootEntry['path'] = substr(Filesystem::normalizePath($path.'/'.$rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507 1507
 
1508 1508
 							// if sharing was disabled for the user we remove the share permissions
1509 1509
 							if (\OCP\Util::isSharingDisabledForUser()) {
@@ -1511,14 +1511,14 @@  discard block
 block discarded – undo
1511 1511
 							}
1512 1512
 
1513 1513
 							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1514
+							$files[] = new FileInfo($path.'/'.$rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515 1515
 						}
1516 1516
 					}
1517 1517
 				}
1518 1518
 			}
1519 1519
 
1520 1520
 			if ($mimetype_filter) {
1521
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1521
+				$files = array_filter($files, function(FileInfo $file) use ($mimetype_filter) {
1522 1522
 					if (strpos($mimetype_filter, '/')) {
1523 1523
 						return $file->getMimetype() === $mimetype_filter;
1524 1524
 					} else {
@@ -1547,7 +1547,7 @@  discard block
 block discarded – undo
1547 1547
 		if ($data instanceof FileInfo) {
1548 1548
 			$data = $data->getData();
1549 1549
 		}
1550
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1550
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1551 1551
 		/**
1552 1552
 		 * @var \OC\Files\Storage\Storage $storage
1553 1553
 		 * @var string $internalPath
@@ -1574,7 +1574,7 @@  discard block
 block discarded – undo
1574 1574
 	 * @return FileInfo[]
1575 1575
 	 */
1576 1576
 	public function search($query) {
1577
-		return $this->searchCommon('search', array('%' . $query . '%'));
1577
+		return $this->searchCommon('search', array('%'.$query.'%'));
1578 1578
 	}
1579 1579
 
1580 1580
 	/**
@@ -1625,10 +1625,10 @@  discard block
 block discarded – undo
1625 1625
 
1626 1626
 			$results = call_user_func_array(array($cache, $method), $args);
1627 1627
 			foreach ($results as $result) {
1628
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1628
+				if (substr($mountPoint.$result['path'], 0, $rootLength + 1) === $this->fakeRoot.'/') {
1629 1629
 					$internalPath = $result['path'];
1630
-					$path = $mountPoint . $result['path'];
1631
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1630
+					$path = $mountPoint.$result['path'];
1631
+					$result['path'] = substr($mountPoint.$result['path'], $rootLength);
1632 1632
 					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633 1633
 					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634 1634
 				}
@@ -1646,8 +1646,8 @@  discard block
 block discarded – undo
1646 1646
 					if ($results) {
1647 1647
 						foreach ($results as $result) {
1648 1648
 							$internalPath = $result['path'];
1649
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
-							$path = rtrim($mountPoint . $internalPath, '/');
1649
+							$result['path'] = rtrim($relativeMountPoint.$result['path'], '/');
1650
+							$path = rtrim($mountPoint.$internalPath, '/');
1651 1651
 							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652 1652
 							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653 1653
 						}
@@ -1668,7 +1668,7 @@  discard block
 block discarded – undo
1668 1668
 	public function getOwner($path) {
1669 1669
 		$info = $this->getFileInfo($path);
1670 1670
 		if (!$info) {
1671
-			throw new NotFoundException($path . ' not found while trying to get owner');
1671
+			throw new NotFoundException($path.' not found while trying to get owner');
1672 1672
 		}
1673 1673
 		return $info->getOwner()->getUID();
1674 1674
 	}
@@ -1702,7 +1702,7 @@  discard block
 block discarded – undo
1702 1702
 	 * @return string
1703 1703
 	 */
1704 1704
 	public function getPath($id) {
1705
-		$id = (int)$id;
1705
+		$id = (int) $id;
1706 1706
 		$manager = Filesystem::getMountManager();
1707 1707
 		$mounts = $manager->findIn($this->fakeRoot);
1708 1708
 		$mounts[] = $manager->find($this->fakeRoot);
@@ -1717,7 +1717,7 @@  discard block
 block discarded – undo
1717 1717
 				$cache = $mount->getStorage()->getCache();
1718 1718
 				$internalPath = $cache->getPathById($id);
1719 1719
 				if (is_string($internalPath)) {
1720
-					$fullPath = $mount->getMountPoint() . $internalPath;
1720
+					$fullPath = $mount->getMountPoint().$internalPath;
1721 1721
 					if (!is_null($path = $this->getRelativePath($fullPath))) {
1722 1722
 						return $path;
1723 1723
 					}
@@ -1760,10 +1760,10 @@  discard block
 block discarded – undo
1760 1760
 		}
1761 1761
 
1762 1762
 		// note: cannot use the view because the target is already locked
1763
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1763
+		$fileId = (int) $targetStorage->getCache()->getId($targetInternalPath);
1764 1764
 		if ($fileId === -1) {
1765 1765
 			// target might not exist, need to check parent instead
1766
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1766
+			$fileId = (int) $targetStorage->getCache()->getId(dirname($targetInternalPath));
1767 1767
 		}
1768 1768
 
1769 1769
 		// check if any of the parents were shared by the current owner (include collections)
@@ -1863,7 +1863,7 @@  discard block
 block discarded – undo
1863 1863
 		$resultPath = '';
1864 1864
 		foreach ($parts as $part) {
1865 1865
 			if ($part) {
1866
-				$resultPath .= '/' . $part;
1866
+				$resultPath .= '/'.$part;
1867 1867
 				$result[] = $resultPath;
1868 1868
 			}
1869 1869
 		}
@@ -2126,16 +2126,16 @@  discard block
 block discarded – undo
2126 2126
 	public function getUidAndFilename($filename) {
2127 2127
 		$info = $this->getFileInfo($filename);
2128 2128
 		if (!$info instanceof \OCP\Files\FileInfo) {
2129
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2129
+			throw new NotFoundException($this->getAbsolutePath($filename).' not found');
2130 2130
 		}
2131 2131
 		$uid = $info->getOwner()->getUID();
2132 2132
 		if ($uid != \OCP\User::getUser()) {
2133 2133
 			Filesystem::initMountPoints($uid);
2134
-			$ownerView = new View('/' . $uid . '/files');
2134
+			$ownerView = new View('/'.$uid.'/files');
2135 2135
 			try {
2136 2136
 				$filename = $ownerView->getPath($info['fileid']);
2137 2137
 			} catch (NotFoundException $e) {
2138
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2138
+				throw new NotFoundException('File with id '.$info['fileid'].' not found for user '.$uid);
2139 2139
 			}
2140 2140
 		}
2141 2141
 		return [$uid, $filename];
@@ -2152,7 +2152,7 @@  discard block
 block discarded – undo
2152 2152
 		$directoryParts = array_filter($directoryParts);
2153 2153
 		foreach ($directoryParts as $key => $part) {
2154 2154
 			$currentPathElements = array_slice($directoryParts, 0, $key);
2155
-			$currentPath = '/' . implode('/', $currentPathElements);
2155
+			$currentPath = '/'.implode('/', $currentPathElements);
2156 2156
 			if ($this->is_file($currentPath)) {
2157 2157
 				return false;
2158 2158
 			}
Please login to merge, or discard this patch.
apps/files_sharing/lib/Helper.php 3 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -303,6 +303,7 @@
 block discarded – undo
303 303
 	 * get default share folder
304 304
 	 *
305 305
 	 * @param \OC\Files\View
306
+	 * @param View $view
306 307
 	 * @return string
307 308
 	 */
308 309
 	public static function getShareFolder($view = null) {
Please login to merge, or discard this patch.
Indentation   +237 added lines, -237 removed lines patch added patch discarded remove patch
@@ -36,242 +36,242 @@
 block discarded – undo
36 36
 
37 37
 class Helper {
38 38
 
39
-	public static function registerHooks() {
40
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook');
41
-		\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
42
-
43
-		\OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
44
-	}
45
-
46
-	/**
47
-	 * Sets up the filesystem and user for public sharing
48
-	 * @param string $token string share token
49
-	 * @param string $relativePath optional path relative to the share
50
-	 * @param string $password optional password
51
-	 * @return array
52
-	 */
53
-	public static function setupFromToken($token, $relativePath = null, $password = null) {
54
-		\OC_User::setIncognitoMode(true);
55
-
56
-		$shareManager = \OC::$server->getShareManager();
57
-
58
-		try {
59
-			$share = $shareManager->getShareByToken($token);
60
-		} catch (ShareNotFound $e) {
61
-			\OC_Response::setStatus(404);
62
-			\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
63
-			exit;
64
-		}
65
-
66
-		\OCP\JSON::checkUserExists($share->getShareOwner());
67
-		\OC_Util::tearDownFS();
68
-		\OC_Util::setupFS($share->getShareOwner());
69
-
70
-
71
-		try {
72
-			$path = Filesystem::getPath($share->getNodeId());
73
-		} catch (NotFoundException $e) {
74
-			\OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
75
-			\OC_Response::setStatus(404);
76
-			\OCP\JSON::error(array('success' => false));
77
-			exit();
78
-		}
79
-
80
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $share->getPassword() !== null) {
81
-			if (!self::authenticate($share, $password)) {
82
-				\OC_Response::setStatus(403);
83
-				\OCP\JSON::error(array('success' => false));
84
-				exit();
85
-			}
86
-		}
87
-
88
-		$basePath = $path;
89
-
90
-		if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
91
-			$path .= Filesystem::normalizePath($relativePath);
92
-		}
93
-
94
-		return array(
95
-			'share' => $share,
96
-			'basePath' => $basePath,
97
-			'realPath' => $path
98
-		);
99
-	}
100
-
101
-	/**
102
-	 * Authenticate link item with the given password
103
-	 * or with the session if no password was given.
104
-	 * @param \OCP\Share\IShare $share
105
-	 * @param string $password optional password
106
-	 *
107
-	 * @return boolean true if authorized, false otherwise
108
-	 */
109
-	public static function authenticate($share, $password = null) {
110
-		$shareManager = \OC::$server->getShareManager();
111
-
112
-		if ($password !== null) {
113
-			if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
114
-				if ($shareManager->checkPassword($share, $password)) {
115
-					\OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId());
116
-					return true;
117
-				}
118
-			}
119
-		} else {
120
-			// not authenticated ?
121
-			if (\OC::$server->getSession()->exists('public_link_authenticated')
122
-				&& \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) {
123
-				return true;
124
-			}
125
-		}
126
-		return false;
127
-	}
128
-
129
-	public static function getSharesFromItem($target) {
130
-		$result = array();
131
-		$owner = Filesystem::getOwner($target);
132
-		Filesystem::initMountPoints($owner);
133
-		$info = Filesystem::getFileInfo($target);
134
-		$ownerView = new View('/'.$owner.'/files');
135
-		if ( $owner !== User::getUser() ) {
136
-			$path = $ownerView->getPath($info['fileid']);
137
-		} else {
138
-			$path = $target;
139
-		}
140
-
141
-
142
-		$ids = array();
143
-		while ($path !== dirname($path)) {
144
-			$info = $ownerView->getFileInfo($path);
145
-			if ($info instanceof \OC\Files\FileInfo) {
146
-				$ids[] = $info['fileid'];
147
-			} else {
148
-				\OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
149
-			}
150
-			$path = dirname($path);
151
-		}
152
-
153
-		if (!empty($ids)) {
154
-
155
-			$idList = array_chunk($ids, 99, true);
156
-
157
-			foreach ($idList as $subList) {
158
-				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
159
-				$query = \OCP\DB::prepare($statement);
160
-				$r = $query->execute();
161
-				$result = array_merge($result, $r->fetchAll());
162
-			}
163
-		}
164
-
165
-		return $result;
166
-	}
167
-
168
-	/**
169
-	 * get the UID of the owner of the file and the path to the file relative to
170
-	 * owners files folder
171
-	 *
172
-	 * @param $filename
173
-	 * @return array
174
-	 * @throws \OC\User\NoUserException
175
-	 */
176
-	public static function getUidAndFilename($filename) {
177
-		$uid = Filesystem::getOwner($filename);
178
-		$userManager = \OC::$server->getUserManager();
179
-		// if the user with the UID doesn't exists, e.g. because the UID points
180
-		// to a remote user with a federated cloud ID we use the current logged-in
181
-		// user. We need a valid local user to create the share
182
-		if (!$userManager->userExists($uid)) {
183
-			$uid = User::getUser();
184
-		}
185
-		Filesystem::initMountPoints($uid);
186
-		if ( $uid !== User::getUser() ) {
187
-			$info = Filesystem::getFileInfo($filename);
188
-			$ownerView = new View('/'.$uid.'/files');
189
-			try {
190
-				$filename = $ownerView->getPath($info['fileid']);
191
-			} catch (NotFoundException $e) {
192
-				$filename = null;
193
-			}
194
-		}
195
-		return [$uid, $filename];
196
-	}
197
-
198
-	/**
199
-	 * Format a path to be relative to the /user/files/ directory
200
-	 * @param string $path the absolute path
201
-	 * @return string e.g. turns '/admin/files/test.txt' into 'test.txt'
202
-	 */
203
-	public static function stripUserFilesPath($path) {
204
-		$trimmed = ltrim($path, '/');
205
-		$split = explode('/', $trimmed);
206
-
207
-		// it is not a file relative to data/user/files
208
-		if (count($split) < 3 || $split[1] !== 'files') {
209
-			return false;
210
-		}
211
-
212
-		$sliced = array_slice($split, 2);
213
-		$relPath = implode('/', $sliced);
214
-
215
-		return $relPath;
216
-	}
217
-
218
-	/**
219
-	 * check if file name already exists and generate unique target
220
-	 *
221
-	 * @param string $path
222
-	 * @param array $excludeList
223
-	 * @param View $view
224
-	 * @return string $path
225
-	 */
226
-	public static function generateUniqueTarget($path, $excludeList, $view) {
227
-		$pathinfo = pathinfo($path);
228
-		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
229
-		$name = $pathinfo['filename'];
230
-		$dir = $pathinfo['dirname'];
231
-		$i = 2;
232
-		while ($view->file_exists($path) || in_array($path, $excludeList)) {
233
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
234
-			$i++;
235
-		}
236
-
237
-		return $path;
238
-	}
239
-
240
-	/**
241
-	 * get default share folder
242
-	 *
243
-	 * @param \OC\Files\View
244
-	 * @return string
245
-	 */
246
-	public static function getShareFolder($view = null) {
247
-		if ($view === null) {
248
-			$view = Filesystem::getView();
249
-		}
250
-		$shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
251
-		$shareFolder = Filesystem::normalizePath($shareFolder);
252
-
253
-		if (!$view->file_exists($shareFolder)) {
254
-			$dir = '';
255
-			$subdirs = explode('/', $shareFolder);
256
-			foreach ($subdirs as $subdir) {
257
-				$dir = $dir . '/' . $subdir;
258
-				if (!$view->is_dir($dir)) {
259
-					$view->mkdir($dir);
260
-				}
261
-			}
262
-		}
263
-
264
-		return $shareFolder;
265
-
266
-	}
267
-
268
-	/**
269
-	 * set default share folder
270
-	 *
271
-	 * @param string $shareFolder
272
-	 */
273
-	public static function setShareFolder($shareFolder) {
274
-		\OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
275
-	}
39
+    public static function registerHooks() {
40
+        \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook');
41
+        \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
42
+
43
+        \OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
44
+    }
45
+
46
+    /**
47
+     * Sets up the filesystem and user for public sharing
48
+     * @param string $token string share token
49
+     * @param string $relativePath optional path relative to the share
50
+     * @param string $password optional password
51
+     * @return array
52
+     */
53
+    public static function setupFromToken($token, $relativePath = null, $password = null) {
54
+        \OC_User::setIncognitoMode(true);
55
+
56
+        $shareManager = \OC::$server->getShareManager();
57
+
58
+        try {
59
+            $share = $shareManager->getShareByToken($token);
60
+        } catch (ShareNotFound $e) {
61
+            \OC_Response::setStatus(404);
62
+            \OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
63
+            exit;
64
+        }
65
+
66
+        \OCP\JSON::checkUserExists($share->getShareOwner());
67
+        \OC_Util::tearDownFS();
68
+        \OC_Util::setupFS($share->getShareOwner());
69
+
70
+
71
+        try {
72
+            $path = Filesystem::getPath($share->getNodeId());
73
+        } catch (NotFoundException $e) {
74
+            \OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
75
+            \OC_Response::setStatus(404);
76
+            \OCP\JSON::error(array('success' => false));
77
+            exit();
78
+        }
79
+
80
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && $share->getPassword() !== null) {
81
+            if (!self::authenticate($share, $password)) {
82
+                \OC_Response::setStatus(403);
83
+                \OCP\JSON::error(array('success' => false));
84
+                exit();
85
+            }
86
+        }
87
+
88
+        $basePath = $path;
89
+
90
+        if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
91
+            $path .= Filesystem::normalizePath($relativePath);
92
+        }
93
+
94
+        return array(
95
+            'share' => $share,
96
+            'basePath' => $basePath,
97
+            'realPath' => $path
98
+        );
99
+    }
100
+
101
+    /**
102
+     * Authenticate link item with the given password
103
+     * or with the session if no password was given.
104
+     * @param \OCP\Share\IShare $share
105
+     * @param string $password optional password
106
+     *
107
+     * @return boolean true if authorized, false otherwise
108
+     */
109
+    public static function authenticate($share, $password = null) {
110
+        $shareManager = \OC::$server->getShareManager();
111
+
112
+        if ($password !== null) {
113
+            if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
114
+                if ($shareManager->checkPassword($share, $password)) {
115
+                    \OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId());
116
+                    return true;
117
+                }
118
+            }
119
+        } else {
120
+            // not authenticated ?
121
+            if (\OC::$server->getSession()->exists('public_link_authenticated')
122
+                && \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) {
123
+                return true;
124
+            }
125
+        }
126
+        return false;
127
+    }
128
+
129
+    public static function getSharesFromItem($target) {
130
+        $result = array();
131
+        $owner = Filesystem::getOwner($target);
132
+        Filesystem::initMountPoints($owner);
133
+        $info = Filesystem::getFileInfo($target);
134
+        $ownerView = new View('/'.$owner.'/files');
135
+        if ( $owner !== User::getUser() ) {
136
+            $path = $ownerView->getPath($info['fileid']);
137
+        } else {
138
+            $path = $target;
139
+        }
140
+
141
+
142
+        $ids = array();
143
+        while ($path !== dirname($path)) {
144
+            $info = $ownerView->getFileInfo($path);
145
+            if ($info instanceof \OC\Files\FileInfo) {
146
+                $ids[] = $info['fileid'];
147
+            } else {
148
+                \OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
149
+            }
150
+            $path = dirname($path);
151
+        }
152
+
153
+        if (!empty($ids)) {
154
+
155
+            $idList = array_chunk($ids, 99, true);
156
+
157
+            foreach ($idList as $subList) {
158
+                $statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
159
+                $query = \OCP\DB::prepare($statement);
160
+                $r = $query->execute();
161
+                $result = array_merge($result, $r->fetchAll());
162
+            }
163
+        }
164
+
165
+        return $result;
166
+    }
167
+
168
+    /**
169
+     * get the UID of the owner of the file and the path to the file relative to
170
+     * owners files folder
171
+     *
172
+     * @param $filename
173
+     * @return array
174
+     * @throws \OC\User\NoUserException
175
+     */
176
+    public static function getUidAndFilename($filename) {
177
+        $uid = Filesystem::getOwner($filename);
178
+        $userManager = \OC::$server->getUserManager();
179
+        // if the user with the UID doesn't exists, e.g. because the UID points
180
+        // to a remote user with a federated cloud ID we use the current logged-in
181
+        // user. We need a valid local user to create the share
182
+        if (!$userManager->userExists($uid)) {
183
+            $uid = User::getUser();
184
+        }
185
+        Filesystem::initMountPoints($uid);
186
+        if ( $uid !== User::getUser() ) {
187
+            $info = Filesystem::getFileInfo($filename);
188
+            $ownerView = new View('/'.$uid.'/files');
189
+            try {
190
+                $filename = $ownerView->getPath($info['fileid']);
191
+            } catch (NotFoundException $e) {
192
+                $filename = null;
193
+            }
194
+        }
195
+        return [$uid, $filename];
196
+    }
197
+
198
+    /**
199
+     * Format a path to be relative to the /user/files/ directory
200
+     * @param string $path the absolute path
201
+     * @return string e.g. turns '/admin/files/test.txt' into 'test.txt'
202
+     */
203
+    public static function stripUserFilesPath($path) {
204
+        $trimmed = ltrim($path, '/');
205
+        $split = explode('/', $trimmed);
206
+
207
+        // it is not a file relative to data/user/files
208
+        if (count($split) < 3 || $split[1] !== 'files') {
209
+            return false;
210
+        }
211
+
212
+        $sliced = array_slice($split, 2);
213
+        $relPath = implode('/', $sliced);
214
+
215
+        return $relPath;
216
+    }
217
+
218
+    /**
219
+     * check if file name already exists and generate unique target
220
+     *
221
+     * @param string $path
222
+     * @param array $excludeList
223
+     * @param View $view
224
+     * @return string $path
225
+     */
226
+    public static function generateUniqueTarget($path, $excludeList, $view) {
227
+        $pathinfo = pathinfo($path);
228
+        $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
229
+        $name = $pathinfo['filename'];
230
+        $dir = $pathinfo['dirname'];
231
+        $i = 2;
232
+        while ($view->file_exists($path) || in_array($path, $excludeList)) {
233
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
234
+            $i++;
235
+        }
236
+
237
+        return $path;
238
+    }
239
+
240
+    /**
241
+     * get default share folder
242
+     *
243
+     * @param \OC\Files\View
244
+     * @return string
245
+     */
246
+    public static function getShareFolder($view = null) {
247
+        if ($view === null) {
248
+            $view = Filesystem::getView();
249
+        }
250
+        $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
251
+        $shareFolder = Filesystem::normalizePath($shareFolder);
252
+
253
+        if (!$view->file_exists($shareFolder)) {
254
+            $dir = '';
255
+            $subdirs = explode('/', $shareFolder);
256
+            foreach ($subdirs as $subdir) {
257
+                $dir = $dir . '/' . $subdir;
258
+                if (!$view->is_dir($dir)) {
259
+                    $view->mkdir($dir);
260
+                }
261
+            }
262
+        }
263
+
264
+        return $shareFolder;
265
+
266
+    }
267
+
268
+    /**
269
+     * set default share folder
270
+     *
271
+     * @param string $shareFolder
272
+     */
273
+    public static function setShareFolder($shareFolder) {
274
+        \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
275
+    }
276 276
 
277 277
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -87,7 +87,7 @@  discard block
 block discarded – undo
87 87
 
88 88
 		$basePath = $path;
89 89
 
90
-		if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
90
+		if ($relativePath !== null && Filesystem::isReadable($basePath.$relativePath)) {
91 91
 			$path .= Filesystem::normalizePath($relativePath);
92 92
 		}
93 93
 
@@ -112,14 +112,14 @@  discard block
 block discarded – undo
112 112
 		if ($password !== null) {
113 113
 			if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
114 114
 				if ($shareManager->checkPassword($share, $password)) {
115
-					\OC::$server->getSession()->set('public_link_authenticated', (string)$share->getId());
115
+					\OC::$server->getSession()->set('public_link_authenticated', (string) $share->getId());
116 116
 					return true;
117 117
 				}
118 118
 			}
119 119
 		} else {
120 120
 			// not authenticated ?
121 121
 			if (\OC::$server->getSession()->exists('public_link_authenticated')
122
-				&& \OC::$server->getSession()->get('public_link_authenticated') !== (string)$share->getId()) {
122
+				&& \OC::$server->getSession()->get('public_link_authenticated') !== (string) $share->getId()) {
123 123
 				return true;
124 124
 			}
125 125
 		}
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 		Filesystem::initMountPoints($owner);
133 133
 		$info = Filesystem::getFileInfo($target);
134 134
 		$ownerView = new View('/'.$owner.'/files');
135
-		if ( $owner !== User::getUser() ) {
135
+		if ($owner !== User::getUser()) {
136 136
 			$path = $ownerView->getPath($info['fileid']);
137 137
 		} else {
138 138
 			$path = $target;
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
 			if ($info instanceof \OC\Files\FileInfo) {
146 146
 				$ids[] = $info['fileid'];
147 147
 			} else {
148
-				\OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
148
+				\OCP\Util::writeLog('sharing', 'No fileinfo available for: '.$path, \OCP\Util::WARN);
149 149
 			}
150 150
 			$path = dirname($path);
151 151
 		}
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 			$idList = array_chunk($ids, 99, true);
156 156
 
157 157
 			foreach ($idList as $subList) {
158
-				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
158
+				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (".implode(',', $subList).") AND `share_type` IN (0, 1, 2)";
159 159
 				$query = \OCP\DB::prepare($statement);
160 160
 				$r = $query->execute();
161 161
 				$result = array_merge($result, $r->fetchAll());
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
 			$uid = User::getUser();
184 184
 		}
185 185
 		Filesystem::initMountPoints($uid);
186
-		if ( $uid !== User::getUser() ) {
186
+		if ($uid !== User::getUser()) {
187 187
 			$info = Filesystem::getFileInfo($filename);
188 188
 			$ownerView = new View('/'.$uid.'/files');
189 189
 			try {
@@ -230,7 +230,7 @@  discard block
 block discarded – undo
230 230
 		$dir = $pathinfo['dirname'];
231 231
 		$i = 2;
232 232
 		while ($view->file_exists($path) || in_array($path, $excludeList)) {
233
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
233
+			$path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext);
234 234
 			$i++;
235 235
 		}
236 236
 
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
 			$dir = '';
255 255
 			$subdirs = explode('/', $shareFolder);
256 256
 			foreach ($subdirs as $subdir) {
257
-				$dir = $dir . '/' . $subdir;
257
+				$dir = $dir.'/'.$subdir;
258 258
 				if (!$view->is_dir($dir)) {
259 259
 					$view->mkdir($dir);
260 260
 				}
Please login to merge, or discard this patch.
apps/dav/lib/SystemTag/SystemTagsObjectMappingCollection.php 3 patches
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -89,6 +89,9 @@
 block discarded – undo
89 89
 		$this->user = $user;
90 90
 	}
91 91
 
92
+	/**
93
+	 * @param string $tagId
94
+	 */
92 95
 	function createFile($tagId, $data = null) {
93 96
 		try {
94 97
 			$tags = $this->tagManager->getTagsByIds([$tagId]);
Please login to merge, or discard this patch.
Indentation   +165 added lines, -165 removed lines patch added patch discarded remove patch
@@ -39,169 +39,169 @@
 block discarded – undo
39 39
  */
40 40
 class SystemTagsObjectMappingCollection implements ICollection {
41 41
 
42
-	/**
43
-	 * @var string
44
-	 */
45
-	private $objectId;
46
-
47
-	/**
48
-	 * @var string
49
-	 */
50
-	private $objectType;
51
-
52
-	/**
53
-	 * @var ISystemTagManager
54
-	 */
55
-	private $tagManager;
56
-
57
-	/**
58
-	 * @var ISystemTagObjectMapper
59
-	 */
60
-	private $tagMapper;
61
-
62
-	/**
63
-	 * User
64
-	 *
65
-	 * @var IUser
66
-	 */
67
-	private $user;
68
-
69
-
70
-	/**
71
-	 * Constructor
72
-	 *
73
-	 * @param string $objectId object id
74
-	 * @param string $objectType object type
75
-	 * @param IUser $user user
76
-	 * @param ISystemTagManager $tagManager tag manager
77
-	 * @param ISystemTagObjectMapper $tagMapper tag mapper
78
-	 */
79
-	public function __construct(
80
-		$objectId,
81
-		$objectType,
82
-		IUser $user,
83
-		ISystemTagManager $tagManager,
84
-		ISystemTagObjectMapper $tagMapper
85
-	) {
86
-		$this->tagManager = $tagManager;
87
-		$this->tagMapper = $tagMapper;
88
-		$this->objectId = $objectId;
89
-		$this->objectType = $objectType;
90
-		$this->user = $user;
91
-	}
92
-
93
-	function createFile($tagId, $data = null) {
94
-		try {
95
-			$tags = $this->tagManager->getTagsByIds([$tagId]);
96
-			$tag = current($tags);
97
-			if (!$this->tagManager->canUserSeeTag($tag, $this->user)) {
98
-				throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
99
-			}
100
-			if (!$this->tagManager->canUserAssignTag($tag, $this->user)) {
101
-				throw new Forbidden('No permission to assign tag ' . $tagId);
102
-			}
103
-
104
-			$this->tagMapper->assignTags($this->objectId, $this->objectType, $tagId);
105
-		} catch (TagNotFoundException $e) {
106
-			throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
107
-		}
108
-	}
109
-
110
-	function createDirectory($name) {
111
-		throw new Forbidden('Permission denied to create collections');
112
-	}
113
-
114
-	function getChild($tagId) {
115
-		try {
116
-			if ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true)) {
117
-				$tag = $this->tagManager->getTagsByIds([$tagId]);
118
-				$tag = current($tag);
119
-				if ($this->tagManager->canUserSeeTag($tag, $this->user)) {
120
-					return $this->makeNode($tag);
121
-				}
122
-			}
123
-			throw new NotFound('Tag with id ' . $tagId . ' not present for object ' . $this->objectId);
124
-		} catch (\InvalidArgumentException $e) {
125
-			throw new BadRequest('Invalid tag id', 0, $e);
126
-		} catch (TagNotFoundException $e) {
127
-			throw new NotFound('Tag with id ' . $tagId . ' not found', 0, $e);
128
-		}
129
-	}
130
-
131
-	function getChildren() {
132
-		$tagIds = current($this->tagMapper->getTagIdsForObjects([$this->objectId], $this->objectType));
133
-		if (empty($tagIds)) {
134
-			return [];
135
-		}
136
-		$tags = $this->tagManager->getTagsByIds($tagIds);
137
-
138
-		// filter out non-visible tags
139
-		$tags = array_filter($tags, function($tag) {
140
-			return $this->tagManager->canUserSeeTag($tag, $this->user);
141
-		});
142
-
143
-		return array_values(array_map(function($tag) {
144
-			return $this->makeNode($tag);
145
-		}, $tags));
146
-	}
147
-
148
-	function childExists($tagId) {
149
-		try {
150
-			$result = ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true));
151
-
152
-			if ($result) {
153
-				$tags = $this->tagManager->getTagsByIds([$tagId]);
154
-				$tag = current($tags);
155
-				if (!$this->tagManager->canUserSeeTag($tag, $this->user)) {
156
-					return false;
157
-				}
158
-			}
159
-
160
-			return $result;
161
-		} catch (\InvalidArgumentException $e) {
162
-			throw new BadRequest('Invalid tag id', 0, $e);
163
-		} catch (TagNotFoundException $e) {
164
-			return false;
165
-		}
166
-	}
167
-
168
-	function delete() {
169
-		throw new Forbidden('Permission denied to delete this collection');
170
-	}
171
-
172
-	function getName() {
173
-		return $this->objectId;
174
-	}
175
-
176
-	function setName($name) {
177
-		throw new Forbidden('Permission denied to rename this collection');
178
-	}
179
-
180
-	/**
181
-	 * Returns the last modification time, as a unix timestamp
182
-	 *
183
-	 * @return int
184
-	 */
185
-	function getLastModified() {
186
-		return null;
187
-	}
188
-
189
-	/**
190
-	 * Create a sabre node for the mapping of the 
191
-	 * given system tag to the collection's object
192
-	 *
193
-	 * @param ISystemTag $tag
194
-	 *
195
-	 * @return SystemTagMappingNode
196
-	 */
197
-	private function makeNode(ISystemTag $tag) {
198
-		return new SystemTagMappingNode(
199
-			$tag,
200
-			$this->objectId,
201
-			$this->objectType,
202
-			$this->user,
203
-			$this->tagManager,
204
-			$this->tagMapper
205
-		);
206
-	}
42
+    /**
43
+     * @var string
44
+     */
45
+    private $objectId;
46
+
47
+    /**
48
+     * @var string
49
+     */
50
+    private $objectType;
51
+
52
+    /**
53
+     * @var ISystemTagManager
54
+     */
55
+    private $tagManager;
56
+
57
+    /**
58
+     * @var ISystemTagObjectMapper
59
+     */
60
+    private $tagMapper;
61
+
62
+    /**
63
+     * User
64
+     *
65
+     * @var IUser
66
+     */
67
+    private $user;
68
+
69
+
70
+    /**
71
+     * Constructor
72
+     *
73
+     * @param string $objectId object id
74
+     * @param string $objectType object type
75
+     * @param IUser $user user
76
+     * @param ISystemTagManager $tagManager tag manager
77
+     * @param ISystemTagObjectMapper $tagMapper tag mapper
78
+     */
79
+    public function __construct(
80
+        $objectId,
81
+        $objectType,
82
+        IUser $user,
83
+        ISystemTagManager $tagManager,
84
+        ISystemTagObjectMapper $tagMapper
85
+    ) {
86
+        $this->tagManager = $tagManager;
87
+        $this->tagMapper = $tagMapper;
88
+        $this->objectId = $objectId;
89
+        $this->objectType = $objectType;
90
+        $this->user = $user;
91
+    }
92
+
93
+    function createFile($tagId, $data = null) {
94
+        try {
95
+            $tags = $this->tagManager->getTagsByIds([$tagId]);
96
+            $tag = current($tags);
97
+            if (!$this->tagManager->canUserSeeTag($tag, $this->user)) {
98
+                throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
99
+            }
100
+            if (!$this->tagManager->canUserAssignTag($tag, $this->user)) {
101
+                throw new Forbidden('No permission to assign tag ' . $tagId);
102
+            }
103
+
104
+            $this->tagMapper->assignTags($this->objectId, $this->objectType, $tagId);
105
+        } catch (TagNotFoundException $e) {
106
+            throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
107
+        }
108
+    }
109
+
110
+    function createDirectory($name) {
111
+        throw new Forbidden('Permission denied to create collections');
112
+    }
113
+
114
+    function getChild($tagId) {
115
+        try {
116
+            if ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true)) {
117
+                $tag = $this->tagManager->getTagsByIds([$tagId]);
118
+                $tag = current($tag);
119
+                if ($this->tagManager->canUserSeeTag($tag, $this->user)) {
120
+                    return $this->makeNode($tag);
121
+                }
122
+            }
123
+            throw new NotFound('Tag with id ' . $tagId . ' not present for object ' . $this->objectId);
124
+        } catch (\InvalidArgumentException $e) {
125
+            throw new BadRequest('Invalid tag id', 0, $e);
126
+        } catch (TagNotFoundException $e) {
127
+            throw new NotFound('Tag with id ' . $tagId . ' not found', 0, $e);
128
+        }
129
+    }
130
+
131
+    function getChildren() {
132
+        $tagIds = current($this->tagMapper->getTagIdsForObjects([$this->objectId], $this->objectType));
133
+        if (empty($tagIds)) {
134
+            return [];
135
+        }
136
+        $tags = $this->tagManager->getTagsByIds($tagIds);
137
+
138
+        // filter out non-visible tags
139
+        $tags = array_filter($tags, function($tag) {
140
+            return $this->tagManager->canUserSeeTag($tag, $this->user);
141
+        });
142
+
143
+        return array_values(array_map(function($tag) {
144
+            return $this->makeNode($tag);
145
+        }, $tags));
146
+    }
147
+
148
+    function childExists($tagId) {
149
+        try {
150
+            $result = ($this->tagMapper->haveTag([$this->objectId], $this->objectType, $tagId, true));
151
+
152
+            if ($result) {
153
+                $tags = $this->tagManager->getTagsByIds([$tagId]);
154
+                $tag = current($tags);
155
+                if (!$this->tagManager->canUserSeeTag($tag, $this->user)) {
156
+                    return false;
157
+                }
158
+            }
159
+
160
+            return $result;
161
+        } catch (\InvalidArgumentException $e) {
162
+            throw new BadRequest('Invalid tag id', 0, $e);
163
+        } catch (TagNotFoundException $e) {
164
+            return false;
165
+        }
166
+    }
167
+
168
+    function delete() {
169
+        throw new Forbidden('Permission denied to delete this collection');
170
+    }
171
+
172
+    function getName() {
173
+        return $this->objectId;
174
+    }
175
+
176
+    function setName($name) {
177
+        throw new Forbidden('Permission denied to rename this collection');
178
+    }
179
+
180
+    /**
181
+     * Returns the last modification time, as a unix timestamp
182
+     *
183
+     * @return int
184
+     */
185
+    function getLastModified() {
186
+        return null;
187
+    }
188
+
189
+    /**
190
+     * Create a sabre node for the mapping of the 
191
+     * given system tag to the collection's object
192
+     *
193
+     * @param ISystemTag $tag
194
+     *
195
+     * @return SystemTagMappingNode
196
+     */
197
+    private function makeNode(ISystemTag $tag) {
198
+        return new SystemTagMappingNode(
199
+            $tag,
200
+            $this->objectId,
201
+            $this->objectType,
202
+            $this->user,
203
+            $this->tagManager,
204
+            $this->tagMapper
205
+        );
206
+    }
207 207
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -95,15 +95,15 @@  discard block
 block discarded – undo
95 95
 			$tags = $this->tagManager->getTagsByIds([$tagId]);
96 96
 			$tag = current($tags);
97 97
 			if (!$this->tagManager->canUserSeeTag($tag, $this->user)) {
98
-				throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
98
+				throw new PreconditionFailed('Tag with id '.$tagId.' does not exist, cannot assign');
99 99
 			}
100 100
 			if (!$this->tagManager->canUserAssignTag($tag, $this->user)) {
101
-				throw new Forbidden('No permission to assign tag ' . $tagId);
101
+				throw new Forbidden('No permission to assign tag '.$tagId);
102 102
 			}
103 103
 
104 104
 			$this->tagMapper->assignTags($this->objectId, $this->objectType, $tagId);
105 105
 		} catch (TagNotFoundException $e) {
106
-			throw new PreconditionFailed('Tag with id ' . $tagId . ' does not exist, cannot assign');
106
+			throw new PreconditionFailed('Tag with id '.$tagId.' does not exist, cannot assign');
107 107
 		}
108 108
 	}
109 109
 
@@ -120,11 +120,11 @@  discard block
 block discarded – undo
120 120
 					return $this->makeNode($tag);
121 121
 				}
122 122
 			}
123
-			throw new NotFound('Tag with id ' . $tagId . ' not present for object ' . $this->objectId);
123
+			throw new NotFound('Tag with id '.$tagId.' not present for object '.$this->objectId);
124 124
 		} catch (\InvalidArgumentException $e) {
125 125
 			throw new BadRequest('Invalid tag id', 0, $e);
126 126
 		} catch (TagNotFoundException $e) {
127
-			throw new NotFound('Tag with id ' . $tagId . ' not found', 0, $e);
127
+			throw new NotFound('Tag with id '.$tagId.' not found', 0, $e);
128 128
 		}
129 129
 	}
130 130
 
Please login to merge, or discard this patch.
lib/private/DB/Migrator.php 3 patches
Doc Comments   +8 added lines patch added patch discarded remove patch
@@ -273,6 +273,10 @@  discard block
 block discarded – undo
273 273
 		return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
274 274
 	}
275 275
 
276
+	/**
277
+	 * @param integer $step
278
+	 * @param integer $max
279
+	 */
276 280
 	protected function emit($sql, $step, $max) {
277 281
 		if ($this->noEmit) {
278 282
 			return;
@@ -283,6 +287,10 @@  discard block
 block discarded – undo
283 287
 		$this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max]));
284 288
 	}
285 289
 
290
+	/**
291
+	 * @param integer $step
292
+	 * @param integer $max
293
+	 */
286 294
 	private function emitCheckStep($tableName, $step, $max) {
287 295
 		if(is_null($this->dispatcher)) {
288 296
 			return;
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
 	 * @return string
138 138
 	 */
139 139
 	protected function generateTemporaryTableName($name) {
140
-		return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
140
+		return $this->config->getSystemValue('dbtableprefix', 'oc_').$name.'_'.$this->random->generate(13, ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
141 141
 	}
142 142
 
143 143
 	/**
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
 				$indexName = $index->getName();
189 189
 			} else {
190 190
 				// avoid conflicts in index names
191
-				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
191
+				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_').$this->random->generate(13, ISecureRandom::CHAR_LOWER);
192 192
 			}
193 193
 			$newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
194 194
 		}
@@ -268,15 +268,15 @@  discard block
 block discarded – undo
268 268
 		$quotedSource = $this->connection->quoteIdentifier($sourceName);
269 269
 		$quotedTarget = $this->connection->quoteIdentifier($targetName);
270 270
 
271
-		$this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
272
-		$this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
271
+		$this->connection->exec('CREATE TABLE '.$quotedTarget.' (LIKE '.$quotedSource.')');
272
+		$this->connection->exec('INSERT INTO '.$quotedTarget.' SELECT * FROM '.$quotedSource);
273 273
 	}
274 274
 
275 275
 	/**
276 276
 	 * @param string $name
277 277
 	 */
278 278
 	protected function dropTable($name) {
279
-		$this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
279
+		$this->connection->exec('DROP TABLE '.$this->connection->quoteIdentifier($name));
280 280
 	}
281 281
 
282 282
 	/**
@@ -284,30 +284,30 @@  discard block
 block discarded – undo
284 284
 	 * @return string
285 285
 	 */
286 286
 	protected function convertStatementToScript($statement) {
287
-		$script = $statement . ';';
287
+		$script = $statement.';';
288 288
 		$script .= PHP_EOL;
289 289
 		$script .= PHP_EOL;
290 290
 		return $script;
291 291
 	}
292 292
 
293 293
 	protected function getFilterExpression() {
294
-		return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
294
+		return '/^'.preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')).'/';
295 295
 	}
296 296
 
297 297
 	protected function emit($sql, $step, $max) {
298 298
 		if ($this->noEmit) {
299 299
 			return;
300 300
 		}
301
-		if(is_null($this->dispatcher)) {
301
+		if (is_null($this->dispatcher)) {
302 302
 			return;
303 303
 		}
304
-		$this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max]));
304
+		$this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max]));
305 305
 	}
306 306
 
307 307
 	private function emitCheckStep($tableName, $step, $max) {
308
-		if(is_null($this->dispatcher)) {
308
+		if (is_null($this->dispatcher)) {
309 309
 			return;
310 310
 		}
311
-		$this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max]));
311
+		$this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max]));
312 312
 	}
313 313
 }
Please login to merge, or discard this patch.
Indentation   +268 added lines, -268 removed lines patch added patch discarded remove patch
@@ -43,272 +43,272 @@
 block discarded – undo
43 43
 
44 44
 class Migrator {
45 45
 
46
-	/** @var \Doctrine\DBAL\Connection */
47
-	protected $connection;
48
-
49
-	/** @var ISecureRandom */
50
-	private $random;
51
-
52
-	/** @var IConfig */
53
-	protected $config;
54
-
55
-	/** @var EventDispatcher  */
56
-	private $dispatcher;
57
-
58
-	/** @var bool */
59
-	private $noEmit = false;
60
-
61
-	/**
62
-	 * @param \Doctrine\DBAL\Connection|Connection $connection
63
-	 * @param ISecureRandom $random
64
-	 * @param IConfig $config
65
-	 * @param EventDispatcher $dispatcher
66
-	 */
67
-	public function __construct(\Doctrine\DBAL\Connection $connection,
68
-								ISecureRandom $random,
69
-								IConfig $config,
70
-								EventDispatcher $dispatcher = null) {
71
-		$this->connection = $connection;
72
-		$this->random = $random;
73
-		$this->config = $config;
74
-		$this->dispatcher = $dispatcher;
75
-	}
76
-
77
-	/**
78
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
79
-	 */
80
-	public function migrate(Schema $targetSchema) {
81
-		$this->noEmit = true;
82
-		$this->applySchema($targetSchema);
83
-	}
84
-
85
-	/**
86
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
87
-	 * @return string
88
-	 */
89
-	public function generateChangeScript(Schema $targetSchema) {
90
-		$schemaDiff = $this->getDiff($targetSchema, $this->connection);
91
-
92
-		$script = '';
93
-		$sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform());
94
-		foreach ($sqls as $sql) {
95
-			$script .= $this->convertStatementToScript($sql);
96
-		}
97
-
98
-		return $script;
99
-	}
100
-
101
-	/**
102
-	 * @param Schema $targetSchema
103
-	 * @throws \OC\DB\MigrationException
104
-	 */
105
-	public function checkMigrate(Schema $targetSchema) {
106
-		$this->noEmit = true;
107
-		/**@var \Doctrine\DBAL\Schema\Table[] $tables */
108
-		$tables = $targetSchema->getTables();
109
-		$filterExpression = $this->getFilterExpression();
110
-		$this->connection->getConfiguration()->
111
-			setFilterSchemaAssetsExpression($filterExpression);
112
-		$existingTables = $this->connection->getSchemaManager()->listTableNames();
113
-
114
-		$step = 0;
115
-		foreach ($tables as $table) {
116
-			if (strpos($table->getName(), '.')) {
117
-				list(, $tableName) = explode('.', $table->getName());
118
-			} else {
119
-				$tableName = $table->getName();
120
-			}
121
-			$this->emitCheckStep($tableName, $step++, count($tables));
122
-			// don't need to check for new tables
123
-			if (array_search($tableName, $existingTables) !== false) {
124
-				$this->checkTableMigrate($table);
125
-			}
126
-		}
127
-	}
128
-
129
-	/**
130
-	 * Create a unique name for the temporary table
131
-	 *
132
-	 * @param string $name
133
-	 * @return string
134
-	 */
135
-	protected function generateTemporaryTableName($name) {
136
-		return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
137
-	}
138
-
139
-	/**
140
-	 * Check the migration of a table on a copy so we can detect errors before messing with the real table
141
-	 *
142
-	 * @param \Doctrine\DBAL\Schema\Table $table
143
-	 * @throws \OC\DB\MigrationException
144
-	 */
145
-	protected function checkTableMigrate(Table $table) {
146
-		$name = $table->getName();
147
-		$tmpName = $this->generateTemporaryTableName($name);
148
-
149
-		$this->copyTable($name, $tmpName);
150
-
151
-		//create the migration schema for the temporary table
152
-		$tmpTable = $this->renameTableSchema($table, $tmpName);
153
-		$schemaConfig = new SchemaConfig();
154
-		$schemaConfig->setName($this->connection->getDatabase());
155
-		$schema = new Schema(array($tmpTable), array(), $schemaConfig);
156
-
157
-		try {
158
-			$this->applySchema($schema);
159
-			$this->dropTable($tmpName);
160
-		} catch (DBALException $e) {
161
-			// pgsql needs to commit it's failed transaction before doing anything else
162
-			if ($this->connection->isTransactionActive()) {
163
-				$this->connection->commit();
164
-			}
165
-			$this->dropTable($tmpName);
166
-			throw new MigrationException($table->getName(), $e->getMessage());
167
-		}
168
-	}
169
-
170
-	/**
171
-	 * @param \Doctrine\DBAL\Schema\Table $table
172
-	 * @param string $newName
173
-	 * @return \Doctrine\DBAL\Schema\Table
174
-	 */
175
-	protected function renameTableSchema(Table $table, $newName) {
176
-		/**
177
-		 * @var \Doctrine\DBAL\Schema\Index[] $indexes
178
-		 */
179
-		$indexes = $table->getIndexes();
180
-		$newIndexes = array();
181
-		foreach ($indexes as $index) {
182
-			if ($index->isPrimary()) {
183
-				// do not rename primary key
184
-				$indexName = $index->getName();
185
-			} else {
186
-				// avoid conflicts in index names
187
-				$indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
188
-			}
189
-			$newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
190
-		}
191
-
192
-		// foreign keys are not supported so we just set it to an empty array
193
-		return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions());
194
-	}
195
-
196
-	public function createSchema() {
197
-		$filterExpression = $this->getFilterExpression();
198
-		$this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
199
-		return $this->connection->getSchemaManager()->createSchema();
200
-	}
201
-
202
-	/**
203
-	 * @param Schema $targetSchema
204
-	 * @param \Doctrine\DBAL\Connection $connection
205
-	 * @return \Doctrine\DBAL\Schema\SchemaDiff
206
-	 * @throws DBALException
207
-	 */
208
-	protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
209
-		// adjust varchar columns with a length higher then getVarcharMaxLength to clob
210
-		foreach ($targetSchema->getTables() as $table) {
211
-			foreach ($table->getColumns() as $column) {
212
-				if ($column->getType() instanceof StringType) {
213
-					if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) {
214
-						$column->setType(Type::getType('text'));
215
-						$column->setLength(null);
216
-					}
217
-				}
218
-			}
219
-		}
220
-
221
-		$filterExpression = $this->getFilterExpression();
222
-		$this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
223
-		$sourceSchema = $connection->getSchemaManager()->createSchema();
224
-
225
-		// remove tables we don't know about
226
-		/** @var $table \Doctrine\DBAL\Schema\Table */
227
-		foreach ($sourceSchema->getTables() as $table) {
228
-			if (!$targetSchema->hasTable($table->getName())) {
229
-				$sourceSchema->dropTable($table->getName());
230
-			}
231
-		}
232
-		// remove sequences we don't know about
233
-		foreach ($sourceSchema->getSequences() as $table) {
234
-			if (!$targetSchema->hasSequence($table->getName())) {
235
-				$sourceSchema->dropSequence($table->getName());
236
-			}
237
-		}
238
-
239
-		$comparator = new Comparator();
240
-		return $comparator->compare($sourceSchema, $targetSchema);
241
-	}
242
-
243
-	/**
244
-	 * @param \Doctrine\DBAL\Schema\Schema $targetSchema
245
-	 * @param \Doctrine\DBAL\Connection $connection
246
-	 */
247
-	protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) {
248
-		if (is_null($connection)) {
249
-			$connection = $this->connection;
250
-		}
251
-
252
-		$schemaDiff = $this->getDiff($targetSchema, $connection);
253
-
254
-		$connection->beginTransaction();
255
-		$sqls = $schemaDiff->toSql($connection->getDatabasePlatform());
256
-		$step = 0;
257
-		foreach ($sqls as $sql) {
258
-			$this->emit($sql, $step++, count($sqls));
259
-			$connection->query($sql);
260
-		}
261
-		$connection->commit();
262
-	}
263
-
264
-	/**
265
-	 * @param string $sourceName
266
-	 * @param string $targetName
267
-	 */
268
-	protected function copyTable($sourceName, $targetName) {
269
-		$quotedSource = $this->connection->quoteIdentifier($sourceName);
270
-		$quotedTarget = $this->connection->quoteIdentifier($targetName);
271
-
272
-		$this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
273
-		$this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
274
-	}
275
-
276
-	/**
277
-	 * @param string $name
278
-	 */
279
-	protected function dropTable($name) {
280
-		$this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
281
-	}
282
-
283
-	/**
284
-	 * @param $statement
285
-	 * @return string
286
-	 */
287
-	protected function convertStatementToScript($statement) {
288
-		$script = $statement . ';';
289
-		$script .= PHP_EOL;
290
-		$script .= PHP_EOL;
291
-		return $script;
292
-	}
293
-
294
-	protected function getFilterExpression() {
295
-		return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
296
-	}
297
-
298
-	protected function emit($sql, $step, $max) {
299
-		if ($this->noEmit) {
300
-			return;
301
-		}
302
-		if(is_null($this->dispatcher)) {
303
-			return;
304
-		}
305
-		$this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max]));
306
-	}
307
-
308
-	private function emitCheckStep($tableName, $step, $max) {
309
-		if(is_null($this->dispatcher)) {
310
-			return;
311
-		}
312
-		$this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max]));
313
-	}
46
+    /** @var \Doctrine\DBAL\Connection */
47
+    protected $connection;
48
+
49
+    /** @var ISecureRandom */
50
+    private $random;
51
+
52
+    /** @var IConfig */
53
+    protected $config;
54
+
55
+    /** @var EventDispatcher  */
56
+    private $dispatcher;
57
+
58
+    /** @var bool */
59
+    private $noEmit = false;
60
+
61
+    /**
62
+     * @param \Doctrine\DBAL\Connection|Connection $connection
63
+     * @param ISecureRandom $random
64
+     * @param IConfig $config
65
+     * @param EventDispatcher $dispatcher
66
+     */
67
+    public function __construct(\Doctrine\DBAL\Connection $connection,
68
+                                ISecureRandom $random,
69
+                                IConfig $config,
70
+                                EventDispatcher $dispatcher = null) {
71
+        $this->connection = $connection;
72
+        $this->random = $random;
73
+        $this->config = $config;
74
+        $this->dispatcher = $dispatcher;
75
+    }
76
+
77
+    /**
78
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
79
+     */
80
+    public function migrate(Schema $targetSchema) {
81
+        $this->noEmit = true;
82
+        $this->applySchema($targetSchema);
83
+    }
84
+
85
+    /**
86
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
87
+     * @return string
88
+     */
89
+    public function generateChangeScript(Schema $targetSchema) {
90
+        $schemaDiff = $this->getDiff($targetSchema, $this->connection);
91
+
92
+        $script = '';
93
+        $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform());
94
+        foreach ($sqls as $sql) {
95
+            $script .= $this->convertStatementToScript($sql);
96
+        }
97
+
98
+        return $script;
99
+    }
100
+
101
+    /**
102
+     * @param Schema $targetSchema
103
+     * @throws \OC\DB\MigrationException
104
+     */
105
+    public function checkMigrate(Schema $targetSchema) {
106
+        $this->noEmit = true;
107
+        /**@var \Doctrine\DBAL\Schema\Table[] $tables */
108
+        $tables = $targetSchema->getTables();
109
+        $filterExpression = $this->getFilterExpression();
110
+        $this->connection->getConfiguration()->
111
+            setFilterSchemaAssetsExpression($filterExpression);
112
+        $existingTables = $this->connection->getSchemaManager()->listTableNames();
113
+
114
+        $step = 0;
115
+        foreach ($tables as $table) {
116
+            if (strpos($table->getName(), '.')) {
117
+                list(, $tableName) = explode('.', $table->getName());
118
+            } else {
119
+                $tableName = $table->getName();
120
+            }
121
+            $this->emitCheckStep($tableName, $step++, count($tables));
122
+            // don't need to check for new tables
123
+            if (array_search($tableName, $existingTables) !== false) {
124
+                $this->checkTableMigrate($table);
125
+            }
126
+        }
127
+    }
128
+
129
+    /**
130
+     * Create a unique name for the temporary table
131
+     *
132
+     * @param string $name
133
+     * @return string
134
+     */
135
+    protected function generateTemporaryTableName($name) {
136
+        return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
137
+    }
138
+
139
+    /**
140
+     * Check the migration of a table on a copy so we can detect errors before messing with the real table
141
+     *
142
+     * @param \Doctrine\DBAL\Schema\Table $table
143
+     * @throws \OC\DB\MigrationException
144
+     */
145
+    protected function checkTableMigrate(Table $table) {
146
+        $name = $table->getName();
147
+        $tmpName = $this->generateTemporaryTableName($name);
148
+
149
+        $this->copyTable($name, $tmpName);
150
+
151
+        //create the migration schema for the temporary table
152
+        $tmpTable = $this->renameTableSchema($table, $tmpName);
153
+        $schemaConfig = new SchemaConfig();
154
+        $schemaConfig->setName($this->connection->getDatabase());
155
+        $schema = new Schema(array($tmpTable), array(), $schemaConfig);
156
+
157
+        try {
158
+            $this->applySchema($schema);
159
+            $this->dropTable($tmpName);
160
+        } catch (DBALException $e) {
161
+            // pgsql needs to commit it's failed transaction before doing anything else
162
+            if ($this->connection->isTransactionActive()) {
163
+                $this->connection->commit();
164
+            }
165
+            $this->dropTable($tmpName);
166
+            throw new MigrationException($table->getName(), $e->getMessage());
167
+        }
168
+    }
169
+
170
+    /**
171
+     * @param \Doctrine\DBAL\Schema\Table $table
172
+     * @param string $newName
173
+     * @return \Doctrine\DBAL\Schema\Table
174
+     */
175
+    protected function renameTableSchema(Table $table, $newName) {
176
+        /**
177
+         * @var \Doctrine\DBAL\Schema\Index[] $indexes
178
+         */
179
+        $indexes = $table->getIndexes();
180
+        $newIndexes = array();
181
+        foreach ($indexes as $index) {
182
+            if ($index->isPrimary()) {
183
+                // do not rename primary key
184
+                $indexName = $index->getName();
185
+            } else {
186
+                // avoid conflicts in index names
187
+                $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER);
188
+            }
189
+            $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
190
+        }
191
+
192
+        // foreign keys are not supported so we just set it to an empty array
193
+        return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions());
194
+    }
195
+
196
+    public function createSchema() {
197
+        $filterExpression = $this->getFilterExpression();
198
+        $this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
199
+        return $this->connection->getSchemaManager()->createSchema();
200
+    }
201
+
202
+    /**
203
+     * @param Schema $targetSchema
204
+     * @param \Doctrine\DBAL\Connection $connection
205
+     * @return \Doctrine\DBAL\Schema\SchemaDiff
206
+     * @throws DBALException
207
+     */
208
+    protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
209
+        // adjust varchar columns with a length higher then getVarcharMaxLength to clob
210
+        foreach ($targetSchema->getTables() as $table) {
211
+            foreach ($table->getColumns() as $column) {
212
+                if ($column->getType() instanceof StringType) {
213
+                    if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) {
214
+                        $column->setType(Type::getType('text'));
215
+                        $column->setLength(null);
216
+                    }
217
+                }
218
+            }
219
+        }
220
+
221
+        $filterExpression = $this->getFilterExpression();
222
+        $this->connection->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
223
+        $sourceSchema = $connection->getSchemaManager()->createSchema();
224
+
225
+        // remove tables we don't know about
226
+        /** @var $table \Doctrine\DBAL\Schema\Table */
227
+        foreach ($sourceSchema->getTables() as $table) {
228
+            if (!$targetSchema->hasTable($table->getName())) {
229
+                $sourceSchema->dropTable($table->getName());
230
+            }
231
+        }
232
+        // remove sequences we don't know about
233
+        foreach ($sourceSchema->getSequences() as $table) {
234
+            if (!$targetSchema->hasSequence($table->getName())) {
235
+                $sourceSchema->dropSequence($table->getName());
236
+            }
237
+        }
238
+
239
+        $comparator = new Comparator();
240
+        return $comparator->compare($sourceSchema, $targetSchema);
241
+    }
242
+
243
+    /**
244
+     * @param \Doctrine\DBAL\Schema\Schema $targetSchema
245
+     * @param \Doctrine\DBAL\Connection $connection
246
+     */
247
+    protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) {
248
+        if (is_null($connection)) {
249
+            $connection = $this->connection;
250
+        }
251
+
252
+        $schemaDiff = $this->getDiff($targetSchema, $connection);
253
+
254
+        $connection->beginTransaction();
255
+        $sqls = $schemaDiff->toSql($connection->getDatabasePlatform());
256
+        $step = 0;
257
+        foreach ($sqls as $sql) {
258
+            $this->emit($sql, $step++, count($sqls));
259
+            $connection->query($sql);
260
+        }
261
+        $connection->commit();
262
+    }
263
+
264
+    /**
265
+     * @param string $sourceName
266
+     * @param string $targetName
267
+     */
268
+    protected function copyTable($sourceName, $targetName) {
269
+        $quotedSource = $this->connection->quoteIdentifier($sourceName);
270
+        $quotedTarget = $this->connection->quoteIdentifier($targetName);
271
+
272
+        $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')');
273
+        $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource);
274
+    }
275
+
276
+    /**
277
+     * @param string $name
278
+     */
279
+    protected function dropTable($name) {
280
+        $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name));
281
+    }
282
+
283
+    /**
284
+     * @param $statement
285
+     * @return string
286
+     */
287
+    protected function convertStatementToScript($statement) {
288
+        $script = $statement . ';';
289
+        $script .= PHP_EOL;
290
+        $script .= PHP_EOL;
291
+        return $script;
292
+    }
293
+
294
+    protected function getFilterExpression() {
295
+        return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
296
+    }
297
+
298
+    protected function emit($sql, $step, $max) {
299
+        if ($this->noEmit) {
300
+            return;
301
+        }
302
+        if(is_null($this->dispatcher)) {
303
+            return;
304
+        }
305
+        $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step+1, $max]));
306
+    }
307
+
308
+    private function emitCheckStep($tableName, $step, $max) {
309
+        if(is_null($this->dispatcher)) {
310
+            return;
311
+        }
312
+        $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step+1, $max]));
313
+    }
314 314
 }
Please login to merge, or discard this patch.
settings/Controller/CheckSetupController.php 3 patches
Doc Comments   +2 added lines, -1 removed lines patch added patch discarded remove patch
@@ -103,6 +103,7 @@  discard block
 block discarded – undo
103 103
 
104 104
 	/**
105 105
 	* Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
106
+	* @param string $sitename
106 107
 	* @return bool
107 108
 	*/
108 109
 	private function isSiteReachable($sitename) {
@@ -285,7 +286,7 @@  discard block
 block discarded – undo
285 286
 
286 287
 	/**
287 288
 	 * @NoCSRFRequired
288
-	 * @return DataResponse
289
+	 * @return DataDisplayResponse
289 290
 	 */
290 291
 	public function getFailedIntegrityCheckFiles() {
291 292
 		if(!$this->checker->isCodeCheckEnforced()) {
Please login to merge, or discard this patch.
Spacing   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 						'www.google.com',
105 105
 						'www.github.com'];
106 106
 
107
-		foreach($siteArray as $site) {
107
+		foreach ($siteArray as $site) {
108 108
 			if ($this->isSiteReachable($site)) {
109 109
 				return true;
110 110
 			}
@@ -117,8 +117,8 @@  discard block
 block discarded – undo
117 117
 	* @return bool
118 118
 	*/
119 119
 	private function isSiteReachable($sitename) {
120
-		$httpSiteName = 'http://' . $sitename . '/';
121
-		$httpsSiteName = 'https://' . $sitename . '/';
120
+		$httpSiteName = 'http://'.$sitename.'/';
121
+		$httpsSiteName = 'https://'.$sitename.'/';
122 122
 
123 123
 		try {
124 124
 			$client = $this->clientService->newClient();
@@ -145,9 +145,9 @@  discard block
 block discarded – undo
145 145
 	 * @return bool
146 146
 	 */
147 147
 	private function isUrandomAvailable() {
148
-		if(@file_exists('/dev/urandom')) {
148
+		if (@file_exists('/dev/urandom')) {
149 149
 			$file = fopen('/dev/urandom', 'rb');
150
-			if($file) {
150
+			if ($file) {
151 151
 				fclose($file);
152 152
 				return true;
153 153
 			}
@@ -178,40 +178,40 @@  discard block
 block discarded – undo
178 178
 		// Don't run check when:
179 179
 		// 1. Server has `has_internet_connection` set to false
180 180
 		// 2. AppStore AND S2S is disabled
181
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
181
+		if (!$this->config->getSystemValue('has_internet_connection', true)) {
182 182
 			return '';
183 183
 		}
184
-		if(!$this->config->getSystemValue('appstoreenabled', true)
184
+		if (!$this->config->getSystemValue('appstoreenabled', true)
185 185
 			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
186 186
 			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
187 187
 			return '';
188 188
 		}
189 189
 
190 190
 		$versionString = $this->getCurlVersion();
191
-		if(isset($versionString['ssl_version'])) {
191
+		if (isset($versionString['ssl_version'])) {
192 192
 			$versionString = $versionString['ssl_version'];
193 193
 		} else {
194 194
 			return '';
195 195
 		}
196 196
 
197
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
198
-		if(!$this->config->getSystemValue('appstoreenabled', true)) {
199
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
197
+		$features = (string) $this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
198
+		if (!$this->config->getSystemValue('appstoreenabled', true)) {
199
+			$features = (string) $this->l10n->t('Federated Cloud Sharing');
200 200
 		}
201 201
 
202 202
 		// Check if at least OpenSSL after 1.01d or 1.0.2b
203
-		if(strpos($versionString, 'OpenSSL/') === 0) {
203
+		if (strpos($versionString, 'OpenSSL/') === 0) {
204 204
 			$majorVersion = substr($versionString, 8, 5);
205 205
 			$patchRelease = substr($versionString, 13, 6);
206 206
 
207
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
207
+			if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
208 208
 				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
209 209
 				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
210 210
 			}
211 211
 		}
212 212
 
213 213
 		// Check if NSS and perform heuristic check
214
-		if(strpos($versionString, 'NSS/') === 0) {
214
+		if (strpos($versionString, 'NSS/') === 0) {
215 215
 			try {
216 216
 				$firstClient = $this->clientService->newClient();
217 217
 				$firstClient->get('https://nextcloud.com/');
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
 				$secondClient = $this->clientService->newClient();
220 220
 				$secondClient->get('https://nextcloud.com/');
221 221
 			} catch (ClientException $e) {
222
-				if($e->getResponse()->getStatusCode() === 400) {
222
+				if ($e->getResponse()->getStatusCode() === 400) {
223 223
 					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
224 224
 				}
225 225
 			}
@@ -314,13 +314,13 @@  discard block
 block discarded – undo
314 314
 	 * @return DataResponse
315 315
 	 */
316 316
 	public function getFailedIntegrityCheckFiles() {
317
-		if(!$this->checker->isCodeCheckEnforced()) {
317
+		if (!$this->checker->isCodeCheckEnforced()) {
318 318
 			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
319 319
 		}
320 320
 
321 321
 		$completeResults = $this->checker->getResults();
322 322
 
323
-		if(!empty($completeResults)) {
323
+		if (!empty($completeResults)) {
324 324
 			$formattedTextResponse = 'Technical information
325 325
 =====================
326 326
 The following list covers which files have failed the integrity check. Please read
@@ -330,12 +330,12 @@  discard block
 block discarded – undo
330 330
 Results
331 331
 =======
332 332
 ';
333
-			foreach($completeResults as $context => $contextResult) {
333
+			foreach ($completeResults as $context => $contextResult) {
334 334
 				$formattedTextResponse .= "- $context\n";
335 335
 
336
-				foreach($contextResult as $category => $result) {
336
+				foreach ($contextResult as $category => $result) {
337 337
 					$formattedTextResponse .= "\t- $category\n";
338
-					if($category !== 'EXCEPTION') {
338
+					if ($category !== 'EXCEPTION') {
339 339
 						foreach ($result as $key => $results) {
340 340
 							$formattedTextResponse .= "\t\t- $key\n";
341 341
 						}
@@ -378,27 +378,27 @@  discard block
 block discarded – undo
378 378
 
379 379
 		$isOpcacheProperlySetUp = true;
380 380
 
381
-		if(!$iniWrapper->getBool('opcache.enable')) {
381
+		if (!$iniWrapper->getBool('opcache.enable')) {
382 382
 			$isOpcacheProperlySetUp = false;
383 383
 		}
384 384
 
385
-		if(!$iniWrapper->getBool('opcache.save_comments')) {
385
+		if (!$iniWrapper->getBool('opcache.save_comments')) {
386 386
 			$isOpcacheProperlySetUp = false;
387 387
 		}
388 388
 
389
-		if(!$iniWrapper->getBool('opcache.enable_cli')) {
389
+		if (!$iniWrapper->getBool('opcache.enable_cli')) {
390 390
 			$isOpcacheProperlySetUp = false;
391 391
 		}
392 392
 
393
-		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
393
+		if ($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
394 394
 			$isOpcacheProperlySetUp = false;
395 395
 		}
396 396
 
397
-		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
397
+		if ($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
398 398
 			$isOpcacheProperlySetUp = false;
399 399
 		}
400 400
 
401
-		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
401
+		if ($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
402 402
 			$isOpcacheProperlySetUp = false;
403 403
 		}
404 404
 
Please login to merge, or discard this patch.
Indentation   +381 added lines, -381 removed lines patch added patch discarded remove patch
@@ -50,282 +50,282 @@  discard block
 block discarded – undo
50 50
  * @package OC\Settings\Controller
51 51
  */
52 52
 class CheckSetupController extends Controller {
53
-	/** @var IConfig */
54
-	private $config;
55
-	/** @var IClientService */
56
-	private $clientService;
57
-	/** @var \OC_Util */
58
-	private $util;
59
-	/** @var IURLGenerator */
60
-	private $urlGenerator;
61
-	/** @var IL10N */
62
-	private $l10n;
63
-	/** @var Checker */
64
-	private $checker;
65
-	/** @var ILogger */
66
-	private $logger;
67
-
68
-	/**
69
-	 * @param string $AppName
70
-	 * @param IRequest $request
71
-	 * @param IConfig $config
72
-	 * @param IClientService $clientService
73
-	 * @param IURLGenerator $urlGenerator
74
-	 * @param \OC_Util $util
75
-	 * @param IL10N $l10n
76
-	 * @param Checker $checker
77
-	 * @param ILogger $logger
78
-	 */
79
-	public function __construct($AppName,
80
-								IRequest $request,
81
-								IConfig $config,
82
-								IClientService $clientService,
83
-								IURLGenerator $urlGenerator,
84
-								\OC_Util $util,
85
-								IL10N $l10n,
86
-								Checker $checker,
87
-								ILogger $logger) {
88
-		parent::__construct($AppName, $request);
89
-		$this->config = $config;
90
-		$this->clientService = $clientService;
91
-		$this->util = $util;
92
-		$this->urlGenerator = $urlGenerator;
93
-		$this->l10n = $l10n;
94
-		$this->checker = $checker;
95
-		$this->logger = $logger;
96
-	}
97
-
98
-	/**
99
-	 * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP
100
-	 * @return bool
101
-	 */
102
-	private function isInternetConnectionWorking() {
103
-		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
104
-			return false;
105
-		}
106
-
107
-		$siteArray = ['www.nextcloud.com',
108
-						'www.google.com',
109
-						'www.github.com'];
110
-
111
-		foreach($siteArray as $site) {
112
-			if ($this->isSiteReachable($site)) {
113
-				return true;
114
-			}
115
-		}
116
-		return false;
117
-	}
118
-
119
-	/**
120
-	* Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
121
-	* @return bool
122
-	*/
123
-	private function isSiteReachable($sitename) {
124
-		$httpSiteName = 'http://' . $sitename . '/';
125
-		$httpsSiteName = 'https://' . $sitename . '/';
126
-
127
-		try {
128
-			$client = $this->clientService->newClient();
129
-			$client->get($httpSiteName);
130
-			$client->get($httpsSiteName);
131
-		} catch (\Exception $e) {
132
-			$this->logger->logException($e, ['app' => 'internet_connection_check']);
133
-			return false;
134
-		}
135
-		return true;
136
-	}
137
-
138
-	/**
139
-	 * Checks whether a local memcache is installed or not
140
-	 * @return bool
141
-	 */
142
-	private function isMemcacheConfigured() {
143
-		return $this->config->getSystemValue('memcache.local', null) !== null;
144
-	}
145
-
146
-	/**
147
-	 * Whether /dev/urandom is available to the PHP controller
148
-	 *
149
-	 * @return bool
150
-	 */
151
-	private function isUrandomAvailable() {
152
-		if(@file_exists('/dev/urandom')) {
153
-			$file = fopen('/dev/urandom', 'rb');
154
-			if($file) {
155
-				fclose($file);
156
-				return true;
157
-			}
158
-		}
159
-
160
-		return false;
161
-	}
162
-
163
-	/**
164
-	 * Public for the sake of unit-testing
165
-	 *
166
-	 * @return array
167
-	 */
168
-	protected function getCurlVersion() {
169
-		return curl_version();
170
-	}
171
-
172
-	/**
173
-	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
174
-	 * have multiple bugs which likely lead to problems in combination with
175
-	 * functionality required by ownCloud such as SNI.
176
-	 *
177
-	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
178
-	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
179
-	 * @return string
180
-	 */
181
-	private function isUsedTlsLibOutdated() {
182
-		// Don't run check when:
183
-		// 1. Server has `has_internet_connection` set to false
184
-		// 2. AppStore AND S2S is disabled
185
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
186
-			return '';
187
-		}
188
-		if(!$this->config->getSystemValue('appstoreenabled', true)
189
-			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
190
-			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
191
-			return '';
192
-		}
193
-
194
-		$versionString = $this->getCurlVersion();
195
-		if(isset($versionString['ssl_version'])) {
196
-			$versionString = $versionString['ssl_version'];
197
-		} else {
198
-			return '';
199
-		}
200
-
201
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
202
-		if(!$this->config->getSystemValue('appstoreenabled', true)) {
203
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
204
-		}
205
-
206
-		// Check if at least OpenSSL after 1.01d or 1.0.2b
207
-		if(strpos($versionString, 'OpenSSL/') === 0) {
208
-			$majorVersion = substr($versionString, 8, 5);
209
-			$patchRelease = substr($versionString, 13, 6);
210
-
211
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
212
-				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
213
-				return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
214
-			}
215
-		}
216
-
217
-		// Check if NSS and perform heuristic check
218
-		if(strpos($versionString, 'NSS/') === 0) {
219
-			try {
220
-				$firstClient = $this->clientService->newClient();
221
-				$firstClient->get('https://nextcloud.com/');
222
-
223
-				$secondClient = $this->clientService->newClient();
224
-				$secondClient->get('https://nextcloud.com/');
225
-			} catch (ClientException $e) {
226
-				if($e->getResponse()->getStatusCode() === 400) {
227
-					return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
228
-				}
229
-			}
230
-		}
231
-
232
-		return '';
233
-	}
234
-
235
-	/**
236
-	 * Whether the version is outdated
237
-	 *
238
-	 * @return bool
239
-	 */
240
-	protected function isPhpOutdated() {
241
-		if (version_compare(PHP_VERSION, '7.0.0', '<')) {
242
-			return true;
243
-		}
244
-
245
-		return false;
246
-	}
247
-
248
-	/**
249
-	 * Whether the php version is still supported (at time of release)
250
-	 * according to: https://secure.php.net/supported-versions.php
251
-	 *
252
-	 * @return array
253
-	 */
254
-	private function isPhpSupported() {
255
-		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
256
-	}
257
-
258
-	/**
259
-	 * Check if the reverse proxy configuration is working as expected
260
-	 *
261
-	 * @return bool
262
-	 */
263
-	private function forwardedForHeadersWorking() {
264
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
265
-		$remoteAddress = $this->request->getRemoteAddress();
266
-
267
-		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
268
-			return false;
269
-		}
270
-
271
-		// either not enabled or working correctly
272
-		return true;
273
-	}
274
-
275
-	/**
276
-	 * Checks if the correct memcache module for PHP is installed. Only
277
-	 * fails if memcached is configured and the working module is not installed.
278
-	 *
279
-	 * @return bool
280
-	 */
281
-	private function isCorrectMemcachedPHPModuleInstalled() {
282
-		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
283
-			return true;
284
-		}
285
-
286
-		// there are two different memcached modules for PHP
287
-		// we only support memcached and not memcache
288
-		// https://code.google.com/p/memcached/wiki/PHPClientComparison
289
-		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
290
-	}
291
-
292
-	/**
293
-	 * Checks if set_time_limit is not disabled.
294
-	 *
295
-	 * @return bool
296
-	 */
297
-	private function isSettimelimitAvailable() {
298
-		if (function_exists('set_time_limit')
299
-			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
300
-			return true;
301
-		}
302
-
303
-		return false;
304
-	}
305
-
306
-	/**
307
-	 * @return RedirectResponse
308
-	 */
309
-	public function rescanFailedIntegrityCheck() {
310
-		$this->checker->runInstanceVerification();
311
-		return new RedirectResponse(
312
-			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
313
-		);
314
-	}
315
-
316
-	/**
317
-	 * @NoCSRFRequired
318
-	 * @return DataResponse
319
-	 */
320
-	public function getFailedIntegrityCheckFiles() {
321
-		if(!$this->checker->isCodeCheckEnforced()) {
322
-			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
323
-		}
324
-
325
-		$completeResults = $this->checker->getResults();
326
-
327
-		if(!empty($completeResults)) {
328
-			$formattedTextResponse = 'Technical information
53
+    /** @var IConfig */
54
+    private $config;
55
+    /** @var IClientService */
56
+    private $clientService;
57
+    /** @var \OC_Util */
58
+    private $util;
59
+    /** @var IURLGenerator */
60
+    private $urlGenerator;
61
+    /** @var IL10N */
62
+    private $l10n;
63
+    /** @var Checker */
64
+    private $checker;
65
+    /** @var ILogger */
66
+    private $logger;
67
+
68
+    /**
69
+     * @param string $AppName
70
+     * @param IRequest $request
71
+     * @param IConfig $config
72
+     * @param IClientService $clientService
73
+     * @param IURLGenerator $urlGenerator
74
+     * @param \OC_Util $util
75
+     * @param IL10N $l10n
76
+     * @param Checker $checker
77
+     * @param ILogger $logger
78
+     */
79
+    public function __construct($AppName,
80
+                                IRequest $request,
81
+                                IConfig $config,
82
+                                IClientService $clientService,
83
+                                IURLGenerator $urlGenerator,
84
+                                \OC_Util $util,
85
+                                IL10N $l10n,
86
+                                Checker $checker,
87
+                                ILogger $logger) {
88
+        parent::__construct($AppName, $request);
89
+        $this->config = $config;
90
+        $this->clientService = $clientService;
91
+        $this->util = $util;
92
+        $this->urlGenerator = $urlGenerator;
93
+        $this->l10n = $l10n;
94
+        $this->checker = $checker;
95
+        $this->logger = $logger;
96
+    }
97
+
98
+    /**
99
+     * Checks if the ownCloud server can connect to the internet using HTTPS and HTTP
100
+     * @return bool
101
+     */
102
+    private function isInternetConnectionWorking() {
103
+        if ($this->config->getSystemValue('has_internet_connection', true) === false) {
104
+            return false;
105
+        }
106
+
107
+        $siteArray = ['www.nextcloud.com',
108
+                        'www.google.com',
109
+                        'www.github.com'];
110
+
111
+        foreach($siteArray as $site) {
112
+            if ($this->isSiteReachable($site)) {
113
+                return true;
114
+            }
115
+        }
116
+        return false;
117
+    }
118
+
119
+    /**
120
+     * Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
121
+     * @return bool
122
+     */
123
+    private function isSiteReachable($sitename) {
124
+        $httpSiteName = 'http://' . $sitename . '/';
125
+        $httpsSiteName = 'https://' . $sitename . '/';
126
+
127
+        try {
128
+            $client = $this->clientService->newClient();
129
+            $client->get($httpSiteName);
130
+            $client->get($httpsSiteName);
131
+        } catch (\Exception $e) {
132
+            $this->logger->logException($e, ['app' => 'internet_connection_check']);
133
+            return false;
134
+        }
135
+        return true;
136
+    }
137
+
138
+    /**
139
+     * Checks whether a local memcache is installed or not
140
+     * @return bool
141
+     */
142
+    private function isMemcacheConfigured() {
143
+        return $this->config->getSystemValue('memcache.local', null) !== null;
144
+    }
145
+
146
+    /**
147
+     * Whether /dev/urandom is available to the PHP controller
148
+     *
149
+     * @return bool
150
+     */
151
+    private function isUrandomAvailable() {
152
+        if(@file_exists('/dev/urandom')) {
153
+            $file = fopen('/dev/urandom', 'rb');
154
+            if($file) {
155
+                fclose($file);
156
+                return true;
157
+            }
158
+        }
159
+
160
+        return false;
161
+    }
162
+
163
+    /**
164
+     * Public for the sake of unit-testing
165
+     *
166
+     * @return array
167
+     */
168
+    protected function getCurlVersion() {
169
+        return curl_version();
170
+    }
171
+
172
+    /**
173
+     * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
174
+     * have multiple bugs which likely lead to problems in combination with
175
+     * functionality required by ownCloud such as SNI.
176
+     *
177
+     * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
178
+     * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
179
+     * @return string
180
+     */
181
+    private function isUsedTlsLibOutdated() {
182
+        // Don't run check when:
183
+        // 1. Server has `has_internet_connection` set to false
184
+        // 2. AppStore AND S2S is disabled
185
+        if(!$this->config->getSystemValue('has_internet_connection', true)) {
186
+            return '';
187
+        }
188
+        if(!$this->config->getSystemValue('appstoreenabled', true)
189
+            && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
190
+            && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
191
+            return '';
192
+        }
193
+
194
+        $versionString = $this->getCurlVersion();
195
+        if(isset($versionString['ssl_version'])) {
196
+            $versionString = $versionString['ssl_version'];
197
+        } else {
198
+            return '';
199
+        }
200
+
201
+        $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
202
+        if(!$this->config->getSystemValue('appstoreenabled', true)) {
203
+            $features = (string)$this->l10n->t('Federated Cloud Sharing');
204
+        }
205
+
206
+        // Check if at least OpenSSL after 1.01d or 1.0.2b
207
+        if(strpos($versionString, 'OpenSSL/') === 0) {
208
+            $majorVersion = substr($versionString, 8, 5);
209
+            $patchRelease = substr($versionString, 13, 6);
210
+
211
+            if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
212
+                ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
213
+                return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['OpenSSL', $versionString, $features]);
214
+            }
215
+        }
216
+
217
+        // Check if NSS and perform heuristic check
218
+        if(strpos($versionString, 'NSS/') === 0) {
219
+            try {
220
+                $firstClient = $this->clientService->newClient();
221
+                $firstClient->get('https://nextcloud.com/');
222
+
223
+                $secondClient = $this->clientService->newClient();
224
+                $secondClient->get('https://nextcloud.com/');
225
+            } catch (ClientException $e) {
226
+                if($e->getResponse()->getStatusCode() === 400) {
227
+                    return (string) $this->l10n->t('cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably.', ['NSS', $versionString, $features]);
228
+                }
229
+            }
230
+        }
231
+
232
+        return '';
233
+    }
234
+
235
+    /**
236
+     * Whether the version is outdated
237
+     *
238
+     * @return bool
239
+     */
240
+    protected function isPhpOutdated() {
241
+        if (version_compare(PHP_VERSION, '7.0.0', '<')) {
242
+            return true;
243
+        }
244
+
245
+        return false;
246
+    }
247
+
248
+    /**
249
+     * Whether the php version is still supported (at time of release)
250
+     * according to: https://secure.php.net/supported-versions.php
251
+     *
252
+     * @return array
253
+     */
254
+    private function isPhpSupported() {
255
+        return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
256
+    }
257
+
258
+    /**
259
+     * Check if the reverse proxy configuration is working as expected
260
+     *
261
+     * @return bool
262
+     */
263
+    private function forwardedForHeadersWorking() {
264
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
265
+        $remoteAddress = $this->request->getRemoteAddress();
266
+
267
+        if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
268
+            return false;
269
+        }
270
+
271
+        // either not enabled or working correctly
272
+        return true;
273
+    }
274
+
275
+    /**
276
+     * Checks if the correct memcache module for PHP is installed. Only
277
+     * fails if memcached is configured and the working module is not installed.
278
+     *
279
+     * @return bool
280
+     */
281
+    private function isCorrectMemcachedPHPModuleInstalled() {
282
+        if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
283
+            return true;
284
+        }
285
+
286
+        // there are two different memcached modules for PHP
287
+        // we only support memcached and not memcache
288
+        // https://code.google.com/p/memcached/wiki/PHPClientComparison
289
+        return !(!extension_loaded('memcached') && extension_loaded('memcache'));
290
+    }
291
+
292
+    /**
293
+     * Checks if set_time_limit is not disabled.
294
+     *
295
+     * @return bool
296
+     */
297
+    private function isSettimelimitAvailable() {
298
+        if (function_exists('set_time_limit')
299
+            && strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
300
+            return true;
301
+        }
302
+
303
+        return false;
304
+    }
305
+
306
+    /**
307
+     * @return RedirectResponse
308
+     */
309
+    public function rescanFailedIntegrityCheck() {
310
+        $this->checker->runInstanceVerification();
311
+        return new RedirectResponse(
312
+            $this->urlGenerator->linkToRoute('settings.AdminSettings.index')
313
+        );
314
+    }
315
+
316
+    /**
317
+     * @NoCSRFRequired
318
+     * @return DataResponse
319
+     */
320
+    public function getFailedIntegrityCheckFiles() {
321
+        if(!$this->checker->isCodeCheckEnforced()) {
322
+            return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
323
+        }
324
+
325
+        $completeResults = $this->checker->getResults();
326
+
327
+        if(!empty($completeResults)) {
328
+            $formattedTextResponse = 'Technical information
329 329
 =====================
330 330
 The following list covers which files have failed the integrity check. Please read
331 331
 the previous linked documentation to learn more about the errors and how to fix
@@ -334,112 +334,112 @@  discard block
 block discarded – undo
334 334
 Results
335 335
 =======
336 336
 ';
337
-			foreach($completeResults as $context => $contextResult) {
338
-				$formattedTextResponse .= "- $context\n";
339
-
340
-				foreach($contextResult as $category => $result) {
341
-					$formattedTextResponse .= "\t- $category\n";
342
-					if($category !== 'EXCEPTION') {
343
-						foreach ($result as $key => $results) {
344
-							$formattedTextResponse .= "\t\t- $key\n";
345
-						}
346
-					} else {
347
-						foreach ($result as $key => $results) {
348
-							$formattedTextResponse .= "\t\t- $results\n";
349
-						}
350
-					}
351
-
352
-				}
353
-			}
354
-
355
-			$formattedTextResponse .= '
337
+            foreach($completeResults as $context => $contextResult) {
338
+                $formattedTextResponse .= "- $context\n";
339
+
340
+                foreach($contextResult as $category => $result) {
341
+                    $formattedTextResponse .= "\t- $category\n";
342
+                    if($category !== 'EXCEPTION') {
343
+                        foreach ($result as $key => $results) {
344
+                            $formattedTextResponse .= "\t\t- $key\n";
345
+                        }
346
+                    } else {
347
+                        foreach ($result as $key => $results) {
348
+                            $formattedTextResponse .= "\t\t- $results\n";
349
+                        }
350
+                    }
351
+
352
+                }
353
+            }
354
+
355
+            $formattedTextResponse .= '
356 356
 Raw output
357 357
 ==========
358 358
 ';
359
-			$formattedTextResponse .= print_r($completeResults, true);
360
-		} else {
361
-			$formattedTextResponse = 'No errors have been found.';
362
-		}
363
-
364
-
365
-		$response = new DataDisplayResponse(
366
-			$formattedTextResponse,
367
-			Http::STATUS_OK,
368
-			[
369
-				'Content-Type' => 'text/plain',
370
-			]
371
-		);
372
-
373
-		return $response;
374
-	}
375
-
376
-	/**
377
-	 * Checks whether a PHP opcache is properly set up
378
-	 * @return bool
379
-	 */
380
-	protected function isOpcacheProperlySetup() {
381
-		$iniWrapper = new IniGetWrapper();
382
-
383
-		$isOpcacheProperlySetUp = true;
384
-
385
-		if(!$iniWrapper->getBool('opcache.enable')) {
386
-			$isOpcacheProperlySetUp = false;
387
-		}
388
-
389
-		if(!$iniWrapper->getBool('opcache.save_comments')) {
390
-			$isOpcacheProperlySetUp = false;
391
-		}
392
-
393
-		if(!$iniWrapper->getBool('opcache.enable_cli')) {
394
-			$isOpcacheProperlySetUp = false;
395
-		}
396
-
397
-		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
398
-			$isOpcacheProperlySetUp = false;
399
-		}
400
-
401
-		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
402
-			$isOpcacheProperlySetUp = false;
403
-		}
404
-
405
-		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
406
-			$isOpcacheProperlySetUp = false;
407
-		}
408
-
409
-		return $isOpcacheProperlySetUp;
410
-	}
411
-
412
-	/**
413
-	 * Check if the required FreeType functions are present
414
-	 * @return bool
415
-	 */
416
-	protected function hasFreeTypeSupport() {
417
-		return function_exists('imagettfbbox') && function_exists('imagettftext');
418
-	}
419
-
420
-	/**
421
-	 * @return DataResponse
422
-	 */
423
-	public function check() {
424
-		return new DataResponse(
425
-			[
426
-				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
427
-				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
428
-				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
429
-				'isUrandomAvailable' => $this->isUrandomAvailable(),
430
-				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
431
-				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
432
-				'phpSupported' => $this->isPhpSupported(),
433
-				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
434
-				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
435
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
436
-				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
437
-				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
438
-				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
439
-				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
440
-				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
441
-				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
442
-			]
443
-		);
444
-	}
359
+            $formattedTextResponse .= print_r($completeResults, true);
360
+        } else {
361
+            $formattedTextResponse = 'No errors have been found.';
362
+        }
363
+
364
+
365
+        $response = new DataDisplayResponse(
366
+            $formattedTextResponse,
367
+            Http::STATUS_OK,
368
+            [
369
+                'Content-Type' => 'text/plain',
370
+            ]
371
+        );
372
+
373
+        return $response;
374
+    }
375
+
376
+    /**
377
+     * Checks whether a PHP opcache is properly set up
378
+     * @return bool
379
+     */
380
+    protected function isOpcacheProperlySetup() {
381
+        $iniWrapper = new IniGetWrapper();
382
+
383
+        $isOpcacheProperlySetUp = true;
384
+
385
+        if(!$iniWrapper->getBool('opcache.enable')) {
386
+            $isOpcacheProperlySetUp = false;
387
+        }
388
+
389
+        if(!$iniWrapper->getBool('opcache.save_comments')) {
390
+            $isOpcacheProperlySetUp = false;
391
+        }
392
+
393
+        if(!$iniWrapper->getBool('opcache.enable_cli')) {
394
+            $isOpcacheProperlySetUp = false;
395
+        }
396
+
397
+        if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
398
+            $isOpcacheProperlySetUp = false;
399
+        }
400
+
401
+        if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
402
+            $isOpcacheProperlySetUp = false;
403
+        }
404
+
405
+        if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
406
+            $isOpcacheProperlySetUp = false;
407
+        }
408
+
409
+        return $isOpcacheProperlySetUp;
410
+    }
411
+
412
+    /**
413
+     * Check if the required FreeType functions are present
414
+     * @return bool
415
+     */
416
+    protected function hasFreeTypeSupport() {
417
+        return function_exists('imagettfbbox') && function_exists('imagettftext');
418
+    }
419
+
420
+    /**
421
+     * @return DataResponse
422
+     */
423
+    public function check() {
424
+        return new DataResponse(
425
+            [
426
+                'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
427
+                'isMemcacheConfigured' => $this->isMemcacheConfigured(),
428
+                'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
429
+                'isUrandomAvailable' => $this->isUrandomAvailable(),
430
+                'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
431
+                'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
432
+                'phpSupported' => $this->isPhpSupported(),
433
+                'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
434
+                'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
435
+                'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
436
+                'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
437
+                'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
438
+                'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
439
+                'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
440
+                'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
441
+                'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
442
+            ]
443
+        );
444
+    }
445 445
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Wizard.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1104,7 +1104,7 @@
 block discarded – undo
1104 1104
 	}
1105 1105
 
1106 1106
 	/**
1107
-	 * @param array $reqs
1107
+	 * @param string[] $reqs
1108 1108
 	 * @return bool
1109 1109
 	 */
1110 1110
 	private function checkRequirements($reqs) {
Please login to merge, or discard this patch.
Indentation   +1305 added lines, -1305 removed lines patch added patch discarded remove patch
@@ -41,1311 +41,1311 @@
 block discarded – undo
41 41
 use OC\ServerNotAvailableException;
42 42
 
43 43
 class Wizard extends LDAPUtility {
44
-	/** @var \OCP\IL10N */
45
-	static protected $l;
46
-	protected $access;
47
-	protected $cr;
48
-	protected $configuration;
49
-	protected $result;
50
-	protected $resultCache = array();
51
-
52
-	const LRESULT_PROCESSED_OK = 2;
53
-	const LRESULT_PROCESSED_INVALID = 3;
54
-	const LRESULT_PROCESSED_SKIP = 4;
55
-
56
-	const LFILTER_LOGIN      = 2;
57
-	const LFILTER_USER_LIST  = 3;
58
-	const LFILTER_GROUP_LIST = 4;
59
-
60
-	const LFILTER_MODE_ASSISTED = 2;
61
-	const LFILTER_MODE_RAW = 1;
62
-
63
-	const LDAP_NW_TIMEOUT = 4;
64
-
65
-	/**
66
-	 * Constructor
67
-	 * @param Configuration $configuration an instance of Configuration
68
-	 * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
69
-	 * @param Access $access
70
-	 */
71
-	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
72
-		parent::__construct($ldap);
73
-		$this->configuration = $configuration;
74
-		if(is_null(Wizard::$l)) {
75
-			Wizard::$l = \OC::$server->getL10N('user_ldap');
76
-		}
77
-		$this->access = $access;
78
-		$this->result = new WizardResult();
79
-	}
80
-
81
-	public function  __destruct() {
82
-		if($this->result->hasChanges()) {
83
-			$this->configuration->saveConfiguration();
84
-		}
85
-	}
86
-
87
-	/**
88
-	 * counts entries in the LDAP directory
89
-	 *
90
-	 * @param string $filter the LDAP search filter
91
-	 * @param string $type a string being either 'users' or 'groups';
92
-	 * @return bool|int
93
-	 * @throws \Exception
94
-	 */
95
-	public function countEntries($filter, $type) {
96
-		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
97
-		if($type === 'users') {
98
-			$reqs[] = 'ldapUserFilter';
99
-		}
100
-		if(!$this->checkRequirements($reqs)) {
101
-			throw new \Exception('Requirements not met', 400);
102
-		}
103
-
104
-		$attr = array('dn'); // default
105
-		$limit = 1001;
106
-		if($type === 'groups') {
107
-			$result =  $this->access->countGroups($filter, $attr, $limit);
108
-		} else if($type === 'users') {
109
-			$result = $this->access->countUsers($filter, $attr, $limit);
110
-		} else if ($type === 'objects') {
111
-			$result = $this->access->countObjects($limit);
112
-		} else {
113
-			throw new \Exception('Internal error: Invalid object type', 500);
114
-		}
115
-
116
-		return $result;
117
-	}
118
-
119
-	/**
120
-	 * formats the return value of a count operation to the string to be
121
-	 * inserted.
122
-	 *
123
-	 * @param bool|int $count
124
-	 * @return int|string
125
-	 */
126
-	private function formatCountResult($count) {
127
-		$formatted = ($count !== false) ? $count : 0;
128
-		if($formatted > 1000) {
129
-			$formatted = '> 1000';
130
-		}
131
-		return $formatted;
132
-	}
133
-
134
-	public function countGroups() {
135
-		$filter = $this->configuration->ldapGroupFilter;
136
-
137
-		if(empty($filter)) {
138
-			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
139
-			$this->result->addChange('ldap_group_count', $output);
140
-			return $this->result;
141
-		}
142
-
143
-		try {
144
-			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
145
-		} catch (\Exception $e) {
146
-			//400 can be ignored, 500 is forwarded
147
-			if($e->getCode() === 500) {
148
-				throw $e;
149
-			}
150
-			return false;
151
-		}
152
-		$output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
153
-		$this->result->addChange('ldap_group_count', $output);
154
-		return $this->result;
155
-	}
156
-
157
-	/**
158
-	 * @return WizardResult
159
-	 * @throws \Exception
160
-	 */
161
-	public function countUsers() {
162
-		$filter = $this->access->getFilterForUserCount();
163
-
164
-		$usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
165
-		$output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
166
-		$this->result->addChange('ldap_user_count', $output);
167
-		return $this->result;
168
-	}
169
-
170
-	/**
171
-	 * counts any objects in the currently set base dn
172
-	 *
173
-	 * @return WizardResult
174
-	 * @throws \Exception
175
-	 */
176
-	public function countInBaseDN() {
177
-		// we don't need to provide a filter in this case
178
-		$total = $this->countEntries(null, 'objects');
179
-		if($total === false) {
180
-			throw new \Exception('invalid results received');
181
-		}
182
-		$this->result->addChange('ldap_test_base', $total);
183
-		return $this->result;
184
-	}
185
-
186
-	/**
187
-	 * counts users with a specified attribute
188
-	 * @param string $attr
189
-	 * @param bool $existsCheck
190
-	 * @return int|bool
191
-	 */
192
-	public function countUsersWithAttribute($attr, $existsCheck = false) {
193
-		if(!$this->checkRequirements(array('ldapHost',
194
-										   'ldapPort',
195
-										   'ldapBase',
196
-										   'ldapUserFilter',
197
-										   ))) {
198
-			return  false;
199
-		}
200
-
201
-		$filter = $this->access->combineFilterWithAnd(array(
202
-			$this->configuration->ldapUserFilter,
203
-			$attr . '=*'
204
-		));
205
-
206
-		$limit = ($existsCheck === false) ? null : 1;
207
-
208
-		return $this->access->countUsers($filter, array('dn'), $limit);
209
-	}
210
-
211
-	/**
212
-	 * detects the display name attribute. If a setting is already present that
213
-	 * returns at least one hit, the detection will be canceled.
214
-	 * @return WizardResult|bool
215
-	 * @throws \Exception
216
-	 */
217
-	public function detectUserDisplayNameAttribute() {
218
-		if(!$this->checkRequirements(array('ldapHost',
219
-										'ldapPort',
220
-										'ldapBase',
221
-										'ldapUserFilter',
222
-										))) {
223
-			return  false;
224
-		}
225
-
226
-		$attr = $this->configuration->ldapUserDisplayName;
227
-		if ($attr !== '' && $attr !== 'displayName') {
228
-			// most likely not the default value with upper case N,
229
-			// verify it still produces a result
230
-			$count = (int)$this->countUsersWithAttribute($attr, true);
231
-			if($count > 0) {
232
-				//no change, but we sent it back to make sure the user interface
233
-				//is still correct, even if the ajax call was cancelled meanwhile
234
-				$this->result->addChange('ldap_display_name', $attr);
235
-				return $this->result;
236
-			}
237
-		}
238
-
239
-		// first attribute that has at least one result wins
240
-		$displayNameAttrs = array('displayname', 'cn');
241
-		foreach ($displayNameAttrs as $attr) {
242
-			$count = (int)$this->countUsersWithAttribute($attr, true);
243
-
244
-			if($count > 0) {
245
-				$this->applyFind('ldap_display_name', $attr);
246
-				return $this->result;
247
-			}
248
-		};
249
-
250
-		throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced LDAP settings.'));
251
-	}
252
-
253
-	/**
254
-	 * detects the most often used email attribute for users applying to the
255
-	 * user list filter. If a setting is already present that returns at least
256
-	 * one hit, the detection will be canceled.
257
-	 * @return WizardResult|bool
258
-	 */
259
-	public function detectEmailAttribute() {
260
-		if(!$this->checkRequirements(array('ldapHost',
261
-										   'ldapPort',
262
-										   'ldapBase',
263
-										   'ldapUserFilter',
264
-										   ))) {
265
-			return  false;
266
-		}
267
-
268
-		$attr = $this->configuration->ldapEmailAttribute;
269
-		if ($attr !== '') {
270
-			$count = (int)$this->countUsersWithAttribute($attr, true);
271
-			if($count > 0) {
272
-				return false;
273
-			}
274
-			$writeLog = true;
275
-		} else {
276
-			$writeLog = false;
277
-		}
278
-
279
-		$emailAttributes = array('mail', 'mailPrimaryAddress');
280
-		$winner = '';
281
-		$maxUsers = 0;
282
-		foreach($emailAttributes as $attr) {
283
-			$count = $this->countUsersWithAttribute($attr);
284
-			if($count > $maxUsers) {
285
-				$maxUsers = $count;
286
-				$winner = $attr;
287
-			}
288
-		}
289
-
290
-		if($winner !== '') {
291
-			$this->applyFind('ldap_email_attr', $winner);
292
-			if($writeLog) {
293
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
294
-					'automatically been reset, because the original value ' .
295
-					'did not return any results.', \OCP\Util::INFO);
296
-			}
297
-		}
298
-
299
-		return $this->result;
300
-	}
301
-
302
-	/**
303
-	 * @return WizardResult
304
-	 * @throws \Exception
305
-	 */
306
-	public function determineAttributes() {
307
-		if(!$this->checkRequirements(array('ldapHost',
308
-										   'ldapPort',
309
-										   'ldapBase',
310
-										   'ldapUserFilter',
311
-										   ))) {
312
-			return  false;
313
-		}
314
-
315
-		$attributes = $this->getUserAttributes();
316
-
317
-		natcasesort($attributes);
318
-		$attributes = array_values($attributes);
319
-
320
-		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
321
-
322
-		$selected = $this->configuration->ldapLoginFilterAttributes;
323
-		if(is_array($selected) && !empty($selected)) {
324
-			$this->result->addChange('ldap_loginfilter_attributes', $selected);
325
-		}
326
-
327
-		return $this->result;
328
-	}
329
-
330
-	/**
331
-	 * detects the available LDAP attributes
332
-	 * @return array|false The instance's WizardResult instance
333
-	 * @throws \Exception
334
-	 */
335
-	private function getUserAttributes() {
336
-		if(!$this->checkRequirements(array('ldapHost',
337
-										   'ldapPort',
338
-										   'ldapBase',
339
-										   'ldapUserFilter',
340
-										   ))) {
341
-			return  false;
342
-		}
343
-		$cr = $this->getConnection();
344
-		if(!$cr) {
345
-			throw new \Exception('Could not connect to LDAP');
346
-		}
347
-
348
-		$base = $this->configuration->ldapBase[0];
349
-		$filter = $this->configuration->ldapUserFilter;
350
-		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
351
-		if(!$this->ldap->isResource($rr)) {
352
-			return false;
353
-		}
354
-		$er = $this->ldap->firstEntry($cr, $rr);
355
-		$attributes = $this->ldap->getAttributes($cr, $er);
356
-		$pureAttributes = array();
357
-		for($i = 0; $i < $attributes['count']; $i++) {
358
-			$pureAttributes[] = $attributes[$i];
359
-		}
360
-
361
-		return $pureAttributes;
362
-	}
363
-
364
-	/**
365
-	 * detects the available LDAP groups
366
-	 * @return WizardResult|false the instance's WizardResult instance
367
-	 */
368
-	public function determineGroupsForGroups() {
369
-		return $this->determineGroups('ldap_groupfilter_groups',
370
-									  'ldapGroupFilterGroups',
371
-									  false);
372
-	}
373
-
374
-	/**
375
-	 * detects the available LDAP groups
376
-	 * @return WizardResult|false the instance's WizardResult instance
377
-	 */
378
-	public function determineGroupsForUsers() {
379
-		return $this->determineGroups('ldap_userfilter_groups',
380
-									  'ldapUserFilterGroups');
381
-	}
382
-
383
-	/**
384
-	 * detects the available LDAP groups
385
-	 * @param string $dbKey
386
-	 * @param string $confKey
387
-	 * @param bool $testMemberOf
388
-	 * @return WizardResult|false the instance's WizardResult instance
389
-	 * @throws \Exception
390
-	 */
391
-	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
392
-		if(!$this->checkRequirements(array('ldapHost',
393
-										   'ldapPort',
394
-										   'ldapBase',
395
-										   ))) {
396
-			return  false;
397
-		}
398
-		$cr = $this->getConnection();
399
-		if(!$cr) {
400
-			throw new \Exception('Could not connect to LDAP');
401
-		}
402
-
403
-		$this->fetchGroups($dbKey, $confKey);
404
-
405
-		if($testMemberOf) {
406
-			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
407
-			$this->result->markChange();
408
-			if(!$this->configuration->hasMemberOfFilterSupport) {
409
-				throw new \Exception('memberOf is not supported by the server');
410
-			}
411
-		}
412
-
413
-		return $this->result;
414
-	}
415
-
416
-	/**
417
-	 * fetches all groups from LDAP and adds them to the result object
418
-	 *
419
-	 * @param string $dbKey
420
-	 * @param string $confKey
421
-	 * @return array $groupEntries
422
-	 * @throws \Exception
423
-	 */
424
-	public function fetchGroups($dbKey, $confKey) {
425
-		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames', 'groupOfUniqueNames');
426
-
427
-		$filterParts = array();
428
-		foreach($obclasses as $obclass) {
429
-			$filterParts[] = 'objectclass='.$obclass;
430
-		}
431
-		//we filter for everything
432
-		//- that looks like a group and
433
-		//- has the group display name set
434
-		$filter = $this->access->combineFilterWithOr($filterParts);
435
-		$filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
436
-
437
-		$groupNames = array();
438
-		$groupEntries = array();
439
-		$limit = 400;
440
-		$offset = 0;
441
-		do {
442
-			// we need to request dn additionally here, otherwise memberOf
443
-			// detection will fail later
444
-			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
445
-			foreach($result as $item) {
446
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
447
-					// just in case - no issue known
448
-					continue;
449
-				}
450
-				$groupNames[] = $item['cn'][0];
451
-				$groupEntries[] = $item;
452
-			}
453
-			$offset += $limit;
454
-		} while ($this->access->hasMoreResults());
455
-
456
-		if(count($groupNames) > 0) {
457
-			natsort($groupNames);
458
-			$this->result->addOptions($dbKey, array_values($groupNames));
459
-		} else {
460
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
461
-		}
462
-
463
-		$setFeatures = $this->configuration->$confKey;
464
-		if(is_array($setFeatures) && !empty($setFeatures)) {
465
-			//something is already configured? pre-select it.
466
-			$this->result->addChange($dbKey, $setFeatures);
467
-		}
468
-		return $groupEntries;
469
-	}
470
-
471
-	public function determineGroupMemberAssoc() {
472
-		if(!$this->checkRequirements(array('ldapHost',
473
-										   'ldapPort',
474
-										   'ldapGroupFilter',
475
-										   ))) {
476
-			return  false;
477
-		}
478
-		$attribute = $this->detectGroupMemberAssoc();
479
-		if($attribute === false) {
480
-			return false;
481
-		}
482
-		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
483
-		$this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
484
-
485
-		return $this->result;
486
-	}
487
-
488
-	/**
489
-	 * Detects the available object classes
490
-	 * @return WizardResult|false the instance's WizardResult instance
491
-	 * @throws \Exception
492
-	 */
493
-	public function determineGroupObjectClasses() {
494
-		if(!$this->checkRequirements(array('ldapHost',
495
-										   'ldapPort',
496
-										   'ldapBase',
497
-										   ))) {
498
-			return  false;
499
-		}
500
-		$cr = $this->getConnection();
501
-		if(!$cr) {
502
-			throw new \Exception('Could not connect to LDAP');
503
-		}
504
-
505
-		$obclasses = array('groupOfNames', 'groupOfUniqueNames', 'group', 'posixGroup', '*');
506
-		$this->determineFeature($obclasses,
507
-								'objectclass',
508
-								'ldap_groupfilter_objectclass',
509
-								'ldapGroupFilterObjectclass',
510
-								false);
511
-
512
-		return $this->result;
513
-	}
514
-
515
-	/**
516
-	 * detects the available object classes
517
-	 * @return WizardResult
518
-	 * @throws \Exception
519
-	 */
520
-	public function determineUserObjectClasses() {
521
-		if(!$this->checkRequirements(array('ldapHost',
522
-										   'ldapPort',
523
-										   'ldapBase',
524
-										   ))) {
525
-			return  false;
526
-		}
527
-		$cr = $this->getConnection();
528
-		if(!$cr) {
529
-			throw new \Exception('Could not connect to LDAP');
530
-		}
531
-
532
-		$obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
533
-						   'user', 'posixAccount', '*');
534
-		$filter = $this->configuration->ldapUserFilter;
535
-		//if filter is empty, it is probably the first time the wizard is called
536
-		//then, apply suggestions.
537
-		$this->determineFeature($obclasses,
538
-								'objectclass',
539
-								'ldap_userfilter_objectclass',
540
-								'ldapUserFilterObjectclass',
541
-								empty($filter));
542
-
543
-		return $this->result;
544
-	}
545
-
546
-	/**
547
-	 * @return WizardResult|false
548
-	 * @throws \Exception
549
-	 */
550
-	public function getGroupFilter() {
551
-		if(!$this->checkRequirements(array('ldapHost',
552
-										   'ldapPort',
553
-										   'ldapBase',
554
-										   ))) {
555
-			return false;
556
-		}
557
-		//make sure the use display name is set
558
-		$displayName = $this->configuration->ldapGroupDisplayName;
559
-		if ($displayName === '') {
560
-			$d = $this->configuration->getDefaults();
561
-			$this->applyFind('ldap_group_display_name',
562
-							 $d['ldap_group_display_name']);
563
-		}
564
-		$filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
565
-
566
-		$this->applyFind('ldap_group_filter', $filter);
567
-		return $this->result;
568
-	}
569
-
570
-	/**
571
-	 * @return WizardResult|false
572
-	 * @throws \Exception
573
-	 */
574
-	public function getUserListFilter() {
575
-		if(!$this->checkRequirements(array('ldapHost',
576
-										   'ldapPort',
577
-										   'ldapBase',
578
-										   ))) {
579
-			return false;
580
-		}
581
-		//make sure the use display name is set
582
-		$displayName = $this->configuration->ldapUserDisplayName;
583
-		if ($displayName === '') {
584
-			$d = $this->configuration->getDefaults();
585
-			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
586
-		}
587
-		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
588
-		if(!$filter) {
589
-			throw new \Exception('Cannot create filter');
590
-		}
591
-
592
-		$this->applyFind('ldap_userlist_filter', $filter);
593
-		return $this->result;
594
-	}
595
-
596
-	/**
597
-	 * @return bool|WizardResult
598
-	 * @throws \Exception
599
-	 */
600
-	public function getUserLoginFilter() {
601
-		if(!$this->checkRequirements(array('ldapHost',
602
-										   'ldapPort',
603
-										   'ldapBase',
604
-										   'ldapUserFilter',
605
-										   ))) {
606
-			return false;
607
-		}
608
-
609
-		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
610
-		if(!$filter) {
611
-			throw new \Exception('Cannot create filter');
612
-		}
613
-
614
-		$this->applyFind('ldap_login_filter', $filter);
615
-		return $this->result;
616
-	}
617
-
618
-	/**
619
-	 * @return bool|WizardResult
620
-	 * @param string $loginName
621
-	 * @throws \Exception
622
-	 */
623
-	public function testLoginName($loginName) {
624
-		if(!$this->checkRequirements(array('ldapHost',
625
-			'ldapPort',
626
-			'ldapBase',
627
-			'ldapLoginFilter',
628
-		))) {
629
-			return false;
630
-		}
631
-
632
-		$cr = $this->access->connection->getConnectionResource();
633
-		if(!$this->ldap->isResource($cr)) {
634
-			throw new \Exception('connection error');
635
-		}
636
-
637
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
638
-			=== false) {
639
-			throw new \Exception('missing placeholder');
640
-		}
641
-
642
-		$users = $this->access->countUsersByLoginName($loginName);
643
-		if($this->ldap->errno($cr) !== 0) {
644
-			throw new \Exception($this->ldap->error($cr));
645
-		}
646
-		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
647
-		$this->result->addChange('ldap_test_loginname', $users);
648
-		$this->result->addChange('ldap_test_effective_filter', $filter);
649
-		return $this->result;
650
-	}
651
-
652
-	/**
653
-	 * Tries to determine the port, requires given Host, User DN and Password
654
-	 * @return WizardResult|false WizardResult on success, false otherwise
655
-	 * @throws \Exception
656
-	 */
657
-	public function guessPortAndTLS() {
658
-		if(!$this->checkRequirements(array('ldapHost',
659
-										   ))) {
660
-			return false;
661
-		}
662
-		$this->checkHost();
663
-		$portSettings = $this->getPortSettingsToTry();
664
-
665
-		if(!is_array($portSettings)) {
666
-			throw new \Exception(print_r($portSettings, true));
667
-		}
668
-
669
-		//proceed from the best configuration and return on first success
670
-		foreach($portSettings as $setting) {
671
-			$p = $setting['port'];
672
-			$t = $setting['tls'];
673
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
674
-			//connectAndBind may throw Exception, it needs to be catched by the
675
-			//callee of this method
676
-
677
-			try {
678
-				$settingsFound = $this->connectAndBind($p, $t);
679
-			} catch (\Exception $e) {
680
-				// any reply other than -1 (= cannot connect) is already okay,
681
-				// because then we found the server
682
-				// unavailable startTLS returns -11
683
-				if($e->getCode() > 0) {
684
-					$settingsFound = true;
685
-				} else {
686
-					throw $e;
687
-				}
688
-			}
689
-
690
-			if ($settingsFound === true) {
691
-				$config = array(
692
-					'ldapPort' => $p,
693
-					'ldapTLS' => intval($t)
694
-				);
695
-				$this->configuration->setConfiguration($config);
696
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
697
-				$this->result->addChange('ldap_port', $p);
698
-				return $this->result;
699
-			}
700
-		}
701
-
702
-		//custom port, undetected (we do not brute force)
703
-		return false;
704
-	}
705
-
706
-	/**
707
-	 * tries to determine a base dn from User DN or LDAP Host
708
-	 * @return WizardResult|false WizardResult on success, false otherwise
709
-	 */
710
-	public function guessBaseDN() {
711
-		if(!$this->checkRequirements(array('ldapHost',
712
-										   'ldapPort',
713
-										   ))) {
714
-			return false;
715
-		}
716
-
717
-		//check whether a DN is given in the agent name (99.9% of all cases)
718
-		$base = null;
719
-		$i = stripos($this->configuration->ldapAgentName, 'dc=');
720
-		if($i !== false) {
721
-			$base = substr($this->configuration->ldapAgentName, $i);
722
-			if($this->testBaseDN($base)) {
723
-				$this->applyFind('ldap_base', $base);
724
-				return $this->result;
725
-			}
726
-		}
727
-
728
-		//this did not help :(
729
-		//Let's see whether we can parse the Host URL and convert the domain to
730
-		//a base DN
731
-		$helper = new Helper(\OC::$server->getConfig());
732
-		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
733
-		if(!$domain) {
734
-			return false;
735
-		}
736
-
737
-		$dparts = explode('.', $domain);
738
-		while(count($dparts) > 0) {
739
-			$base2 = 'dc=' . implode(',dc=', $dparts);
740
-			if ($base !== $base2 && $this->testBaseDN($base2)) {
741
-				$this->applyFind('ldap_base', $base2);
742
-				return $this->result;
743
-			}
744
-			array_shift($dparts);
745
-		}
746
-
747
-		return false;
748
-	}
749
-
750
-	/**
751
-	 * sets the found value for the configuration key in the WizardResult
752
-	 * as well as in the Configuration instance
753
-	 * @param string $key the configuration key
754
-	 * @param string $value the (detected) value
755
-	 *
756
-	 */
757
-	private function applyFind($key, $value) {
758
-		$this->result->addChange($key, $value);
759
-		$this->configuration->setConfiguration(array($key => $value));
760
-	}
761
-
762
-	/**
763
-	 * Checks, whether a port was entered in the Host configuration
764
-	 * field. In this case the port will be stripped off, but also stored as
765
-	 * setting.
766
-	 */
767
-	private function checkHost() {
768
-		$host = $this->configuration->ldapHost;
769
-		$hostInfo = parse_url($host);
770
-
771
-		//removes Port from Host
772
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
773
-			$port = $hostInfo['port'];
774
-			$host = str_replace(':'.$port, '', $host);
775
-			$this->applyFind('ldap_host', $host);
776
-			$this->applyFind('ldap_port', $port);
777
-		}
778
-	}
779
-
780
-	/**
781
-	 * tries to detect the group member association attribute which is
782
-	 * one of 'uniqueMember', 'memberUid', 'member', 'gidNumber'
783
-	 * @return string|false, string with the attribute name, false on error
784
-	 * @throws \Exception
785
-	 */
786
-	private function detectGroupMemberAssoc() {
787
-		$possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'gidNumber');
788
-		$filter = $this->configuration->ldapGroupFilter;
789
-		if(empty($filter)) {
790
-			return false;
791
-		}
792
-		$cr = $this->getConnection();
793
-		if(!$cr) {
794
-			throw new \Exception('Could not connect to LDAP');
795
-		}
796
-		$base = $this->configuration->ldapBase[0];
797
-		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
798
-		if(!$this->ldap->isResource($rr)) {
799
-			return false;
800
-		}
801
-		$er = $this->ldap->firstEntry($cr, $rr);
802
-		while(is_resource($er)) {
803
-			$this->ldap->getDN($cr, $er);
804
-			$attrs = $this->ldap->getAttributes($cr, $er);
805
-			$result = array();
806
-			$possibleAttrsCount = count($possibleAttrs);
807
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
808
-				if(isset($attrs[$possibleAttrs[$i]])) {
809
-					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
810
-				}
811
-			}
812
-			if(!empty($result)) {
813
-				natsort($result);
814
-				return key($result);
815
-			}
816
-
817
-			$er = $this->ldap->nextEntry($cr, $er);
818
-		}
819
-
820
-		return false;
821
-	}
822
-
823
-	/**
824
-	 * Checks whether for a given BaseDN results will be returned
825
-	 * @param string $base the BaseDN to test
826
-	 * @return bool true on success, false otherwise
827
-	 * @throws \Exception
828
-	 */
829
-	private function testBaseDN($base) {
830
-		$cr = $this->getConnection();
831
-		if(!$cr) {
832
-			throw new \Exception('Could not connect to LDAP');
833
-		}
834
-
835
-		//base is there, let's validate it. If we search for anything, we should
836
-		//get a result set > 0 on a proper base
837
-		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
838
-		if(!$this->ldap->isResource($rr)) {
839
-			$errorNo  = $this->ldap->errno($cr);
840
-			$errorMsg = $this->ldap->error($cr);
841
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
842
-							' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
843
-			return false;
844
-		}
845
-		$entries = $this->ldap->countEntries($cr, $rr);
846
-		return ($entries !== false) && ($entries > 0);
847
-	}
848
-
849
-	/**
850
-	 * Checks whether the server supports memberOf in LDAP Filter.
851
-	 * Note: at least in OpenLDAP, availability of memberOf is dependent on
852
-	 * a configured objectClass. I.e. not necessarily for all available groups
853
-	 * memberOf does work.
854
-	 *
855
-	 * @return bool true if it does, false otherwise
856
-	 * @throws \Exception
857
-	 */
858
-	private function testMemberOf() {
859
-		$cr = $this->getConnection();
860
-		if(!$cr) {
861
-			throw new \Exception('Could not connect to LDAP');
862
-		}
863
-		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
864
-		if(is_int($result) &&  $result > 0) {
865
-			return true;
866
-		}
867
-		return false;
868
-	}
869
-
870
-	/**
871
-	 * creates an LDAP Filter from given configuration
872
-	 * @param integer $filterType int, for which use case the filter shall be created
873
-	 * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
874
-	 * self::LFILTER_GROUP_LIST
875
-	 * @return string|false string with the filter on success, false otherwise
876
-	 * @throws \Exception
877
-	 */
878
-	private function composeLdapFilter($filterType) {
879
-		$filter = '';
880
-		$parts = 0;
881
-		switch ($filterType) {
882
-			case self::LFILTER_USER_LIST:
883
-				$objcs = $this->configuration->ldapUserFilterObjectclass;
884
-				//glue objectclasses
885
-				if(is_array($objcs) && count($objcs) > 0) {
886
-					$filter .= '(|';
887
-					foreach($objcs as $objc) {
888
-						$filter .= '(objectclass=' . $objc . ')';
889
-					}
890
-					$filter .= ')';
891
-					$parts++;
892
-				}
893
-				//glue group memberships
894
-				if($this->configuration->hasMemberOfFilterSupport) {
895
-					$cns = $this->configuration->ldapUserFilterGroups;
896
-					if(is_array($cns) && count($cns) > 0) {
897
-						$filter .= '(|';
898
-						$cr = $this->getConnection();
899
-						if(!$cr) {
900
-							throw new \Exception('Could not connect to LDAP');
901
-						}
902
-						$base = $this->configuration->ldapBase[0];
903
-						foreach($cns as $cn) {
904
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
905
-							if(!$this->ldap->isResource($rr)) {
906
-								continue;
907
-							}
908
-							$er = $this->ldap->firstEntry($cr, $rr);
909
-							$attrs = $this->ldap->getAttributes($cr, $er);
910
-							$dn = $this->ldap->getDN($cr, $er);
911
-							if ($dn === false || $dn === '') {
912
-								continue;
913
-							}
914
-							$filterPart = '(memberof=' . $dn . ')';
915
-							if(isset($attrs['primaryGroupToken'])) {
916
-								$pgt = $attrs['primaryGroupToken'][0];
917
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
918
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
919
-							}
920
-							$filter .= $filterPart;
921
-						}
922
-						$filter .= ')';
923
-					}
924
-					$parts++;
925
-				}
926
-				//wrap parts in AND condition
927
-				if($parts > 1) {
928
-					$filter = '(&' . $filter . ')';
929
-				}
930
-				if ($filter === '') {
931
-					$filter = '(objectclass=*)';
932
-				}
933
-				break;
934
-
935
-			case self::LFILTER_GROUP_LIST:
936
-				$objcs = $this->configuration->ldapGroupFilterObjectclass;
937
-				//glue objectclasses
938
-				if(is_array($objcs) && count($objcs) > 0) {
939
-					$filter .= '(|';
940
-					foreach($objcs as $objc) {
941
-						$filter .= '(objectclass=' . $objc . ')';
942
-					}
943
-					$filter .= ')';
944
-					$parts++;
945
-				}
946
-				//glue group memberships
947
-				$cns = $this->configuration->ldapGroupFilterGroups;
948
-				if(is_array($cns) && count($cns) > 0) {
949
-					$filter .= '(|';
950
-					foreach($cns as $cn) {
951
-						$filter .= '(cn=' . $cn . ')';
952
-					}
953
-					$filter .= ')';
954
-				}
955
-				$parts++;
956
-				//wrap parts in AND condition
957
-				if($parts > 1) {
958
-					$filter = '(&' . $filter . ')';
959
-				}
960
-				break;
961
-
962
-			case self::LFILTER_LOGIN:
963
-				$ulf = $this->configuration->ldapUserFilter;
964
-				$loginpart = '=%uid';
965
-				$filterUsername = '';
966
-				$userAttributes = $this->getUserAttributes();
967
-				$userAttributes = array_change_key_case(array_flip($userAttributes));
968
-				$parts = 0;
969
-
970
-				if($this->configuration->ldapLoginFilterUsername === '1') {
971
-					$attr = '';
972
-					if(isset($userAttributes['uid'])) {
973
-						$attr = 'uid';
974
-					} else if(isset($userAttributes['samaccountname'])) {
975
-						$attr = 'samaccountname';
976
-					} else if(isset($userAttributes['cn'])) {
977
-						//fallback
978
-						$attr = 'cn';
979
-					}
980
-					if ($attr !== '') {
981
-						$filterUsername = '(' . $attr . $loginpart . ')';
982
-						$parts++;
983
-					}
984
-				}
985
-
986
-				$filterEmail = '';
987
-				if($this->configuration->ldapLoginFilterEmail === '1') {
988
-					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
989
-					$parts++;
990
-				}
991
-
992
-				$filterAttributes = '';
993
-				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
994
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
995
-					$filterAttributes = '(|';
996
-					foreach($attrsToFilter as $attribute) {
997
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
998
-					}
999
-					$filterAttributes .= ')';
1000
-					$parts++;
1001
-				}
1002
-
1003
-				$filterLogin = '';
1004
-				if($parts > 1) {
1005
-					$filterLogin = '(|';
1006
-				}
1007
-				$filterLogin .= $filterUsername;
1008
-				$filterLogin .= $filterEmail;
1009
-				$filterLogin .= $filterAttributes;
1010
-				if($parts > 1) {
1011
-					$filterLogin .= ')';
1012
-				}
1013
-
1014
-				$filter = '(&'.$ulf.$filterLogin.')';
1015
-				break;
1016
-		}
1017
-
1018
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1019
-
1020
-		return $filter;
1021
-	}
1022
-
1023
-	/**
1024
-	 * Connects and Binds to an LDAP Server
1025
-	 *
1026
-	 * @param int $port the port to connect with
1027
-	 * @param bool $tls whether startTLS is to be used
1028
-	 * @return bool
1029
-	 * @throws \Exception
1030
-	 */
1031
-	private function connectAndBind($port, $tls) {
1032
-		//connect, does not really trigger any server communication
1033
-		$host = $this->configuration->ldapHost;
1034
-		$hostInfo = parse_url($host);
1035
-		if(!$hostInfo) {
1036
-			throw new \Exception(self::$l->t('Invalid Host'));
1037
-		}
1038
-		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039
-		$cr = $this->ldap->connect($host, $port);
1040
-		if(!is_resource($cr)) {
1041
-			throw new \Exception(self::$l->t('Invalid Host'));
1042
-		}
1043
-
1044
-		//set LDAP options
1045
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1046
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1047
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1048
-
1049
-		try {
1050
-			if($tls) {
1051
-				$isTlsWorking = @$this->ldap->startTls($cr);
1052
-				if(!$isTlsWorking) {
1053
-					return false;
1054
-				}
1055
-			}
1056
-
1057
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1058
-			//interesting part: do the bind!
1059
-			$login = $this->ldap->bind($cr,
1060
-				$this->configuration->ldapAgentName,
1061
-				$this->configuration->ldapAgentPassword
1062
-			);
1063
-			$errNo = $this->ldap->errno($cr);
1064
-			$error = ldap_error($cr);
1065
-			$this->ldap->unbind($cr);
1066
-		} catch(ServerNotAvailableException $e) {
1067
-			return false;
1068
-		}
1069
-
1070
-		if($login === true) {
1071
-			$this->ldap->unbind($cr);
1072
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1073
-			return true;
1074
-		}
1075
-
1076
-		if($errNo === -1) {
1077
-			//host, port or TLS wrong
1078
-			return false;
1079
-		}
1080
-		throw new \Exception($error, $errNo);
1081
-	}
1082
-
1083
-	/**
1084
-	 * checks whether a valid combination of agent and password has been
1085
-	 * provided (either two values or nothing for anonymous connect)
1086
-	 * @return bool, true if everything is fine, false otherwise
1087
-	 */
1088
-	private function checkAgentRequirements() {
1089
-		$agent = $this->configuration->ldapAgentName;
1090
-		$pwd = $this->configuration->ldapAgentPassword;
1091
-
1092
-		return
1093
-			($agent !== '' && $pwd !== '')
1094
-			||  ($agent === '' && $pwd === '')
1095
-		;
1096
-	}
1097
-
1098
-	/**
1099
-	 * @param array $reqs
1100
-	 * @return bool
1101
-	 */
1102
-	private function checkRequirements($reqs) {
1103
-		$this->checkAgentRequirements();
1104
-		foreach($reqs as $option) {
1105
-			$value = $this->configuration->$option;
1106
-			if(empty($value)) {
1107
-				return false;
1108
-			}
1109
-		}
1110
-		return true;
1111
-	}
1112
-
1113
-	/**
1114
-	 * does a cumulativeSearch on LDAP to get different values of a
1115
-	 * specified attribute
1116
-	 * @param string[] $filters array, the filters that shall be used in the search
1117
-	 * @param string $attr the attribute of which a list of values shall be returned
1118
-	 * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1119
-	 * The lower, the faster
1120
-	 * @param string $maxF string. if not null, this variable will have the filter that
1121
-	 * yields most result entries
1122
-	 * @return array|false an array with the values on success, false otherwise
1123
-	 */
1124
-	public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1125
-		$dnRead = array();
1126
-		$foundItems = array();
1127
-		$maxEntries = 0;
1128
-		if(!is_array($this->configuration->ldapBase)
1129
-		   || !isset($this->configuration->ldapBase[0])) {
1130
-			return false;
1131
-		}
1132
-		$base = $this->configuration->ldapBase[0];
1133
-		$cr = $this->getConnection();
1134
-		if(!$this->ldap->isResource($cr)) {
1135
-			return false;
1136
-		}
1137
-		$lastFilter = null;
1138
-		if(isset($filters[count($filters)-1])) {
1139
-			$lastFilter = $filters[count($filters)-1];
1140
-		}
1141
-		foreach($filters as $filter) {
1142
-			if($lastFilter === $filter && count($foundItems) > 0) {
1143
-				//skip when the filter is a wildcard and results were found
1144
-				continue;
1145
-			}
1146
-			// 20k limit for performance and reason
1147
-			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1148
-			if(!$this->ldap->isResource($rr)) {
1149
-				continue;
1150
-			}
1151
-			$entries = $this->ldap->countEntries($cr, $rr);
1152
-			$getEntryFunc = 'firstEntry';
1153
-			if(($entries !== false) && ($entries > 0)) {
1154
-				if(!is_null($maxF) && $entries > $maxEntries) {
1155
-					$maxEntries = $entries;
1156
-					$maxF = $filter;
1157
-				}
1158
-				$dnReadCount = 0;
1159
-				do {
1160
-					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1161
-					$getEntryFunc = 'nextEntry';
1162
-					if(!$this->ldap->isResource($entry)) {
1163
-						continue 2;
1164
-					}
1165
-					$rr = $entry; //will be expected by nextEntry next round
1166
-					$attributes = $this->ldap->getAttributes($cr, $entry);
1167
-					$dn = $this->ldap->getDN($cr, $entry);
1168
-					if($dn === false || in_array($dn, $dnRead)) {
1169
-						continue;
1170
-					}
1171
-					$newItems = array();
1172
-					$state = $this->getAttributeValuesFromEntry($attributes,
1173
-																$attr,
1174
-																$newItems);
1175
-					$dnReadCount++;
1176
-					$foundItems = array_merge($foundItems, $newItems);
1177
-					$this->resultCache[$dn][$attr] = $newItems;
1178
-					$dnRead[] = $dn;
1179
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1180
-						|| $this->ldap->isResource($entry))
1181
-						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1182
-			}
1183
-		}
1184
-
1185
-		return array_unique($foundItems);
1186
-	}
1187
-
1188
-	/**
1189
-	 * determines if and which $attr are available on the LDAP server
1190
-	 * @param string[] $objectclasses the objectclasses to use as search filter
1191
-	 * @param string $attr the attribute to look for
1192
-	 * @param string $dbkey the dbkey of the setting the feature is connected to
1193
-	 * @param string $confkey the confkey counterpart for the $dbkey as used in the
1194
-	 * Configuration class
1195
-	 * @param bool $po whether the objectClass with most result entries
1196
-	 * shall be pre-selected via the result
1197
-	 * @return array|false list of found items.
1198
-	 * @throws \Exception
1199
-	 */
1200
-	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1201
-		$cr = $this->getConnection();
1202
-		if(!$cr) {
1203
-			throw new \Exception('Could not connect to LDAP');
1204
-		}
1205
-		$p = 'objectclass=';
1206
-		foreach($objectclasses as $key => $value) {
1207
-			$objectclasses[$key] = $p.$value;
1208
-		}
1209
-		$maxEntryObjC = '';
1210
-
1211
-		//how deep to dig?
1212
-		//When looking for objectclasses, testing few entries is sufficient,
1213
-		$dig = 3;
1214
-
1215
-		$availableFeatures =
1216
-			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1217
-											   $dig, $maxEntryObjC);
1218
-		if(is_array($availableFeatures)
1219
-		   && count($availableFeatures) > 0) {
1220
-			natcasesort($availableFeatures);
1221
-			//natcasesort keeps indices, but we must get rid of them for proper
1222
-			//sorting in the web UI. Therefore: array_values
1223
-			$this->result->addOptions($dbkey, array_values($availableFeatures));
1224
-		} else {
1225
-			throw new \Exception(self::$l->t('Could not find the desired feature'));
1226
-		}
1227
-
1228
-		$setFeatures = $this->configuration->$confkey;
1229
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1230
-			//something is already configured? pre-select it.
1231
-			$this->result->addChange($dbkey, $setFeatures);
1232
-		} else if ($po && $maxEntryObjC !== '') {
1233
-			//pre-select objectclass with most result entries
1234
-			$maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1235
-			$this->applyFind($dbkey, $maxEntryObjC);
1236
-			$this->result->addChange($dbkey, $maxEntryObjC);
1237
-		}
1238
-
1239
-		return $availableFeatures;
1240
-	}
1241
-
1242
-	/**
1243
-	 * appends a list of values fr
1244
-	 * @param resource $result the return value from ldap_get_attributes
1245
-	 * @param string $attribute the attribute values to look for
1246
-	 * @param array &$known new values will be appended here
1247
-	 * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1248
-	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1249
-	 */
1250
-	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1251
-		if(!is_array($result)
1252
-		   || !isset($result['count'])
1253
-		   || !$result['count'] > 0) {
1254
-			return self::LRESULT_PROCESSED_INVALID;
1255
-		}
1256
-
1257
-		// strtolower on all keys for proper comparison
1258
-		$result = \OCP\Util::mb_array_change_key_case($result);
1259
-		$attribute = strtolower($attribute);
1260
-		if(isset($result[$attribute])) {
1261
-			foreach($result[$attribute] as $key => $val) {
1262
-				if($key === 'count') {
1263
-					continue;
1264
-				}
1265
-				if(!in_array($val, $known)) {
1266
-					$known[] = $val;
1267
-				}
1268
-			}
1269
-			return self::LRESULT_PROCESSED_OK;
1270
-		} else {
1271
-			return self::LRESULT_PROCESSED_SKIP;
1272
-		}
1273
-	}
1274
-
1275
-	/**
1276
-	 * @return bool|mixed
1277
-	 */
1278
-	private function getConnection() {
1279
-		if(!is_null($this->cr)) {
1280
-			return $this->cr;
1281
-		}
1282
-
1283
-		$cr = $this->ldap->connect(
1284
-			$this->configuration->ldapHost,
1285
-			$this->configuration->ldapPort
1286
-		);
1287
-
1288
-		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1289
-		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1290
-		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1291
-		if($this->configuration->ldapTLS === 1) {
1292
-			$this->ldap->startTls($cr);
1293
-		}
1294
-
1295
-		$lo = @$this->ldap->bind($cr,
1296
-								 $this->configuration->ldapAgentName,
1297
-								 $this->configuration->ldapAgentPassword);
1298
-		if($lo === true) {
1299
-			$this->$cr = $cr;
1300
-			return $cr;
1301
-		}
1302
-
1303
-		return false;
1304
-	}
1305
-
1306
-	/**
1307
-	 * @return array
1308
-	 */
1309
-	private function getDefaultLdapPortSettings() {
1310
-		static $settings = array(
1311
-								array('port' => 7636, 'tls' => false),
1312
-								array('port' =>  636, 'tls' => false),
1313
-								array('port' => 7389, 'tls' => true),
1314
-								array('port' =>  389, 'tls' => true),
1315
-								array('port' => 7389, 'tls' => false),
1316
-								array('port' =>  389, 'tls' => false),
1317
-						  );
1318
-		return $settings;
1319
-	}
1320
-
1321
-	/**
1322
-	 * @return array
1323
-	 */
1324
-	private function getPortSettingsToTry() {
1325
-		//389 ← LDAP / Unencrypted or StartTLS
1326
-		//636 ← LDAPS / SSL
1327
-		//7xxx ← UCS. need to be checked first, because both ports may be open
1328
-		$host = $this->configuration->ldapHost;
1329
-		$port = intval($this->configuration->ldapPort);
1330
-		$portSettings = array();
1331
-
1332
-		//In case the port is already provided, we will check this first
1333
-		if($port > 0) {
1334
-			$hostInfo = parse_url($host);
1335
-			if(!(is_array($hostInfo)
1336
-				&& isset($hostInfo['scheme'])
1337
-				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1338
-				$portSettings[] = array('port' => $port, 'tls' => true);
1339
-			}
1340
-			$portSettings[] =array('port' => $port, 'tls' => false);
1341
-		}
1342
-
1343
-		//default ports
1344
-		$portSettings = array_merge($portSettings,
1345
-		                            $this->getDefaultLdapPortSettings());
1346
-
1347
-		return $portSettings;
1348
-	}
44
+    /** @var \OCP\IL10N */
45
+    static protected $l;
46
+    protected $access;
47
+    protected $cr;
48
+    protected $configuration;
49
+    protected $result;
50
+    protected $resultCache = array();
51
+
52
+    const LRESULT_PROCESSED_OK = 2;
53
+    const LRESULT_PROCESSED_INVALID = 3;
54
+    const LRESULT_PROCESSED_SKIP = 4;
55
+
56
+    const LFILTER_LOGIN      = 2;
57
+    const LFILTER_USER_LIST  = 3;
58
+    const LFILTER_GROUP_LIST = 4;
59
+
60
+    const LFILTER_MODE_ASSISTED = 2;
61
+    const LFILTER_MODE_RAW = 1;
62
+
63
+    const LDAP_NW_TIMEOUT = 4;
64
+
65
+    /**
66
+     * Constructor
67
+     * @param Configuration $configuration an instance of Configuration
68
+     * @param ILDAPWrapper $ldap an instance of ILDAPWrapper
69
+     * @param Access $access
70
+     */
71
+    public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
72
+        parent::__construct($ldap);
73
+        $this->configuration = $configuration;
74
+        if(is_null(Wizard::$l)) {
75
+            Wizard::$l = \OC::$server->getL10N('user_ldap');
76
+        }
77
+        $this->access = $access;
78
+        $this->result = new WizardResult();
79
+    }
80
+
81
+    public function  __destruct() {
82
+        if($this->result->hasChanges()) {
83
+            $this->configuration->saveConfiguration();
84
+        }
85
+    }
86
+
87
+    /**
88
+     * counts entries in the LDAP directory
89
+     *
90
+     * @param string $filter the LDAP search filter
91
+     * @param string $type a string being either 'users' or 'groups';
92
+     * @return bool|int
93
+     * @throws \Exception
94
+     */
95
+    public function countEntries($filter, $type) {
96
+        $reqs = array('ldapHost', 'ldapPort', 'ldapBase');
97
+        if($type === 'users') {
98
+            $reqs[] = 'ldapUserFilter';
99
+        }
100
+        if(!$this->checkRequirements($reqs)) {
101
+            throw new \Exception('Requirements not met', 400);
102
+        }
103
+
104
+        $attr = array('dn'); // default
105
+        $limit = 1001;
106
+        if($type === 'groups') {
107
+            $result =  $this->access->countGroups($filter, $attr, $limit);
108
+        } else if($type === 'users') {
109
+            $result = $this->access->countUsers($filter, $attr, $limit);
110
+        } else if ($type === 'objects') {
111
+            $result = $this->access->countObjects($limit);
112
+        } else {
113
+            throw new \Exception('Internal error: Invalid object type', 500);
114
+        }
115
+
116
+        return $result;
117
+    }
118
+
119
+    /**
120
+     * formats the return value of a count operation to the string to be
121
+     * inserted.
122
+     *
123
+     * @param bool|int $count
124
+     * @return int|string
125
+     */
126
+    private function formatCountResult($count) {
127
+        $formatted = ($count !== false) ? $count : 0;
128
+        if($formatted > 1000) {
129
+            $formatted = '> 1000';
130
+        }
131
+        return $formatted;
132
+    }
133
+
134
+    public function countGroups() {
135
+        $filter = $this->configuration->ldapGroupFilter;
136
+
137
+        if(empty($filter)) {
138
+            $output = self::$l->n('%s group found', '%s groups found', 0, array(0));
139
+            $this->result->addChange('ldap_group_count', $output);
140
+            return $this->result;
141
+        }
142
+
143
+        try {
144
+            $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
145
+        } catch (\Exception $e) {
146
+            //400 can be ignored, 500 is forwarded
147
+            if($e->getCode() === 500) {
148
+                throw $e;
149
+            }
150
+            return false;
151
+        }
152
+        $output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
153
+        $this->result->addChange('ldap_group_count', $output);
154
+        return $this->result;
155
+    }
156
+
157
+    /**
158
+     * @return WizardResult
159
+     * @throws \Exception
160
+     */
161
+    public function countUsers() {
162
+        $filter = $this->access->getFilterForUserCount();
163
+
164
+        $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
165
+        $output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
166
+        $this->result->addChange('ldap_user_count', $output);
167
+        return $this->result;
168
+    }
169
+
170
+    /**
171
+     * counts any objects in the currently set base dn
172
+     *
173
+     * @return WizardResult
174
+     * @throws \Exception
175
+     */
176
+    public function countInBaseDN() {
177
+        // we don't need to provide a filter in this case
178
+        $total = $this->countEntries(null, 'objects');
179
+        if($total === false) {
180
+            throw new \Exception('invalid results received');
181
+        }
182
+        $this->result->addChange('ldap_test_base', $total);
183
+        return $this->result;
184
+    }
185
+
186
+    /**
187
+     * counts users with a specified attribute
188
+     * @param string $attr
189
+     * @param bool $existsCheck
190
+     * @return int|bool
191
+     */
192
+    public function countUsersWithAttribute($attr, $existsCheck = false) {
193
+        if(!$this->checkRequirements(array('ldapHost',
194
+                                            'ldapPort',
195
+                                            'ldapBase',
196
+                                            'ldapUserFilter',
197
+                                            ))) {
198
+            return  false;
199
+        }
200
+
201
+        $filter = $this->access->combineFilterWithAnd(array(
202
+            $this->configuration->ldapUserFilter,
203
+            $attr . '=*'
204
+        ));
205
+
206
+        $limit = ($existsCheck === false) ? null : 1;
207
+
208
+        return $this->access->countUsers($filter, array('dn'), $limit);
209
+    }
210
+
211
+    /**
212
+     * detects the display name attribute. If a setting is already present that
213
+     * returns at least one hit, the detection will be canceled.
214
+     * @return WizardResult|bool
215
+     * @throws \Exception
216
+     */
217
+    public function detectUserDisplayNameAttribute() {
218
+        if(!$this->checkRequirements(array('ldapHost',
219
+                                        'ldapPort',
220
+                                        'ldapBase',
221
+                                        'ldapUserFilter',
222
+                                        ))) {
223
+            return  false;
224
+        }
225
+
226
+        $attr = $this->configuration->ldapUserDisplayName;
227
+        if ($attr !== '' && $attr !== 'displayName') {
228
+            // most likely not the default value with upper case N,
229
+            // verify it still produces a result
230
+            $count = (int)$this->countUsersWithAttribute($attr, true);
231
+            if($count > 0) {
232
+                //no change, but we sent it back to make sure the user interface
233
+                //is still correct, even if the ajax call was cancelled meanwhile
234
+                $this->result->addChange('ldap_display_name', $attr);
235
+                return $this->result;
236
+            }
237
+        }
238
+
239
+        // first attribute that has at least one result wins
240
+        $displayNameAttrs = array('displayname', 'cn');
241
+        foreach ($displayNameAttrs as $attr) {
242
+            $count = (int)$this->countUsersWithAttribute($attr, true);
243
+
244
+            if($count > 0) {
245
+                $this->applyFind('ldap_display_name', $attr);
246
+                return $this->result;
247
+            }
248
+        };
249
+
250
+        throw new \Exception(self::$l->t('Could not detect user display name attribute. Please specify it yourself in advanced LDAP settings.'));
251
+    }
252
+
253
+    /**
254
+     * detects the most often used email attribute for users applying to the
255
+     * user list filter. If a setting is already present that returns at least
256
+     * one hit, the detection will be canceled.
257
+     * @return WizardResult|bool
258
+     */
259
+    public function detectEmailAttribute() {
260
+        if(!$this->checkRequirements(array('ldapHost',
261
+                                            'ldapPort',
262
+                                            'ldapBase',
263
+                                            'ldapUserFilter',
264
+                                            ))) {
265
+            return  false;
266
+        }
267
+
268
+        $attr = $this->configuration->ldapEmailAttribute;
269
+        if ($attr !== '') {
270
+            $count = (int)$this->countUsersWithAttribute($attr, true);
271
+            if($count > 0) {
272
+                return false;
273
+            }
274
+            $writeLog = true;
275
+        } else {
276
+            $writeLog = false;
277
+        }
278
+
279
+        $emailAttributes = array('mail', 'mailPrimaryAddress');
280
+        $winner = '';
281
+        $maxUsers = 0;
282
+        foreach($emailAttributes as $attr) {
283
+            $count = $this->countUsersWithAttribute($attr);
284
+            if($count > $maxUsers) {
285
+                $maxUsers = $count;
286
+                $winner = $attr;
287
+            }
288
+        }
289
+
290
+        if($winner !== '') {
291
+            $this->applyFind('ldap_email_attr', $winner);
292
+            if($writeLog) {
293
+                \OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
294
+                    'automatically been reset, because the original value ' .
295
+                    'did not return any results.', \OCP\Util::INFO);
296
+            }
297
+        }
298
+
299
+        return $this->result;
300
+    }
301
+
302
+    /**
303
+     * @return WizardResult
304
+     * @throws \Exception
305
+     */
306
+    public function determineAttributes() {
307
+        if(!$this->checkRequirements(array('ldapHost',
308
+                                            'ldapPort',
309
+                                            'ldapBase',
310
+                                            'ldapUserFilter',
311
+                                            ))) {
312
+            return  false;
313
+        }
314
+
315
+        $attributes = $this->getUserAttributes();
316
+
317
+        natcasesort($attributes);
318
+        $attributes = array_values($attributes);
319
+
320
+        $this->result->addOptions('ldap_loginfilter_attributes', $attributes);
321
+
322
+        $selected = $this->configuration->ldapLoginFilterAttributes;
323
+        if(is_array($selected) && !empty($selected)) {
324
+            $this->result->addChange('ldap_loginfilter_attributes', $selected);
325
+        }
326
+
327
+        return $this->result;
328
+    }
329
+
330
+    /**
331
+     * detects the available LDAP attributes
332
+     * @return array|false The instance's WizardResult instance
333
+     * @throws \Exception
334
+     */
335
+    private function getUserAttributes() {
336
+        if(!$this->checkRequirements(array('ldapHost',
337
+                                            'ldapPort',
338
+                                            'ldapBase',
339
+                                            'ldapUserFilter',
340
+                                            ))) {
341
+            return  false;
342
+        }
343
+        $cr = $this->getConnection();
344
+        if(!$cr) {
345
+            throw new \Exception('Could not connect to LDAP');
346
+        }
347
+
348
+        $base = $this->configuration->ldapBase[0];
349
+        $filter = $this->configuration->ldapUserFilter;
350
+        $rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
351
+        if(!$this->ldap->isResource($rr)) {
352
+            return false;
353
+        }
354
+        $er = $this->ldap->firstEntry($cr, $rr);
355
+        $attributes = $this->ldap->getAttributes($cr, $er);
356
+        $pureAttributes = array();
357
+        for($i = 0; $i < $attributes['count']; $i++) {
358
+            $pureAttributes[] = $attributes[$i];
359
+        }
360
+
361
+        return $pureAttributes;
362
+    }
363
+
364
+    /**
365
+     * detects the available LDAP groups
366
+     * @return WizardResult|false the instance's WizardResult instance
367
+     */
368
+    public function determineGroupsForGroups() {
369
+        return $this->determineGroups('ldap_groupfilter_groups',
370
+                                        'ldapGroupFilterGroups',
371
+                                        false);
372
+    }
373
+
374
+    /**
375
+     * detects the available LDAP groups
376
+     * @return WizardResult|false the instance's WizardResult instance
377
+     */
378
+    public function determineGroupsForUsers() {
379
+        return $this->determineGroups('ldap_userfilter_groups',
380
+                                        'ldapUserFilterGroups');
381
+    }
382
+
383
+    /**
384
+     * detects the available LDAP groups
385
+     * @param string $dbKey
386
+     * @param string $confKey
387
+     * @param bool $testMemberOf
388
+     * @return WizardResult|false the instance's WizardResult instance
389
+     * @throws \Exception
390
+     */
391
+    private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
392
+        if(!$this->checkRequirements(array('ldapHost',
393
+                                            'ldapPort',
394
+                                            'ldapBase',
395
+                                            ))) {
396
+            return  false;
397
+        }
398
+        $cr = $this->getConnection();
399
+        if(!$cr) {
400
+            throw new \Exception('Could not connect to LDAP');
401
+        }
402
+
403
+        $this->fetchGroups($dbKey, $confKey);
404
+
405
+        if($testMemberOf) {
406
+            $this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
407
+            $this->result->markChange();
408
+            if(!$this->configuration->hasMemberOfFilterSupport) {
409
+                throw new \Exception('memberOf is not supported by the server');
410
+            }
411
+        }
412
+
413
+        return $this->result;
414
+    }
415
+
416
+    /**
417
+     * fetches all groups from LDAP and adds them to the result object
418
+     *
419
+     * @param string $dbKey
420
+     * @param string $confKey
421
+     * @return array $groupEntries
422
+     * @throws \Exception
423
+     */
424
+    public function fetchGroups($dbKey, $confKey) {
425
+        $obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames', 'groupOfUniqueNames');
426
+
427
+        $filterParts = array();
428
+        foreach($obclasses as $obclass) {
429
+            $filterParts[] = 'objectclass='.$obclass;
430
+        }
431
+        //we filter for everything
432
+        //- that looks like a group and
433
+        //- has the group display name set
434
+        $filter = $this->access->combineFilterWithOr($filterParts);
435
+        $filter = $this->access->combineFilterWithAnd(array($filter, 'cn=*'));
436
+
437
+        $groupNames = array();
438
+        $groupEntries = array();
439
+        $limit = 400;
440
+        $offset = 0;
441
+        do {
442
+            // we need to request dn additionally here, otherwise memberOf
443
+            // detection will fail later
444
+            $result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
445
+            foreach($result as $item) {
446
+                if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
447
+                    // just in case - no issue known
448
+                    continue;
449
+                }
450
+                $groupNames[] = $item['cn'][0];
451
+                $groupEntries[] = $item;
452
+            }
453
+            $offset += $limit;
454
+        } while ($this->access->hasMoreResults());
455
+
456
+        if(count($groupNames) > 0) {
457
+            natsort($groupNames);
458
+            $this->result->addOptions($dbKey, array_values($groupNames));
459
+        } else {
460
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
461
+        }
462
+
463
+        $setFeatures = $this->configuration->$confKey;
464
+        if(is_array($setFeatures) && !empty($setFeatures)) {
465
+            //something is already configured? pre-select it.
466
+            $this->result->addChange($dbKey, $setFeatures);
467
+        }
468
+        return $groupEntries;
469
+    }
470
+
471
+    public function determineGroupMemberAssoc() {
472
+        if(!$this->checkRequirements(array('ldapHost',
473
+                                            'ldapPort',
474
+                                            'ldapGroupFilter',
475
+                                            ))) {
476
+            return  false;
477
+        }
478
+        $attribute = $this->detectGroupMemberAssoc();
479
+        if($attribute === false) {
480
+            return false;
481
+        }
482
+        $this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
483
+        $this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
484
+
485
+        return $this->result;
486
+    }
487
+
488
+    /**
489
+     * Detects the available object classes
490
+     * @return WizardResult|false the instance's WizardResult instance
491
+     * @throws \Exception
492
+     */
493
+    public function determineGroupObjectClasses() {
494
+        if(!$this->checkRequirements(array('ldapHost',
495
+                                            'ldapPort',
496
+                                            'ldapBase',
497
+                                            ))) {
498
+            return  false;
499
+        }
500
+        $cr = $this->getConnection();
501
+        if(!$cr) {
502
+            throw new \Exception('Could not connect to LDAP');
503
+        }
504
+
505
+        $obclasses = array('groupOfNames', 'groupOfUniqueNames', 'group', 'posixGroup', '*');
506
+        $this->determineFeature($obclasses,
507
+                                'objectclass',
508
+                                'ldap_groupfilter_objectclass',
509
+                                'ldapGroupFilterObjectclass',
510
+                                false);
511
+
512
+        return $this->result;
513
+    }
514
+
515
+    /**
516
+     * detects the available object classes
517
+     * @return WizardResult
518
+     * @throws \Exception
519
+     */
520
+    public function determineUserObjectClasses() {
521
+        if(!$this->checkRequirements(array('ldapHost',
522
+                                            'ldapPort',
523
+                                            'ldapBase',
524
+                                            ))) {
525
+            return  false;
526
+        }
527
+        $cr = $this->getConnection();
528
+        if(!$cr) {
529
+            throw new \Exception('Could not connect to LDAP');
530
+        }
531
+
532
+        $obclasses = array('inetOrgPerson', 'person', 'organizationalPerson',
533
+                            'user', 'posixAccount', '*');
534
+        $filter = $this->configuration->ldapUserFilter;
535
+        //if filter is empty, it is probably the first time the wizard is called
536
+        //then, apply suggestions.
537
+        $this->determineFeature($obclasses,
538
+                                'objectclass',
539
+                                'ldap_userfilter_objectclass',
540
+                                'ldapUserFilterObjectclass',
541
+                                empty($filter));
542
+
543
+        return $this->result;
544
+    }
545
+
546
+    /**
547
+     * @return WizardResult|false
548
+     * @throws \Exception
549
+     */
550
+    public function getGroupFilter() {
551
+        if(!$this->checkRequirements(array('ldapHost',
552
+                                            'ldapPort',
553
+                                            'ldapBase',
554
+                                            ))) {
555
+            return false;
556
+        }
557
+        //make sure the use display name is set
558
+        $displayName = $this->configuration->ldapGroupDisplayName;
559
+        if ($displayName === '') {
560
+            $d = $this->configuration->getDefaults();
561
+            $this->applyFind('ldap_group_display_name',
562
+                                $d['ldap_group_display_name']);
563
+        }
564
+        $filter = $this->composeLdapFilter(self::LFILTER_GROUP_LIST);
565
+
566
+        $this->applyFind('ldap_group_filter', $filter);
567
+        return $this->result;
568
+    }
569
+
570
+    /**
571
+     * @return WizardResult|false
572
+     * @throws \Exception
573
+     */
574
+    public function getUserListFilter() {
575
+        if(!$this->checkRequirements(array('ldapHost',
576
+                                            'ldapPort',
577
+                                            'ldapBase',
578
+                                            ))) {
579
+            return false;
580
+        }
581
+        //make sure the use display name is set
582
+        $displayName = $this->configuration->ldapUserDisplayName;
583
+        if ($displayName === '') {
584
+            $d = $this->configuration->getDefaults();
585
+            $this->applyFind('ldap_display_name', $d['ldap_display_name']);
586
+        }
587
+        $filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
588
+        if(!$filter) {
589
+            throw new \Exception('Cannot create filter');
590
+        }
591
+
592
+        $this->applyFind('ldap_userlist_filter', $filter);
593
+        return $this->result;
594
+    }
595
+
596
+    /**
597
+     * @return bool|WizardResult
598
+     * @throws \Exception
599
+     */
600
+    public function getUserLoginFilter() {
601
+        if(!$this->checkRequirements(array('ldapHost',
602
+                                            'ldapPort',
603
+                                            'ldapBase',
604
+                                            'ldapUserFilter',
605
+                                            ))) {
606
+            return false;
607
+        }
608
+
609
+        $filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
610
+        if(!$filter) {
611
+            throw new \Exception('Cannot create filter');
612
+        }
613
+
614
+        $this->applyFind('ldap_login_filter', $filter);
615
+        return $this->result;
616
+    }
617
+
618
+    /**
619
+     * @return bool|WizardResult
620
+     * @param string $loginName
621
+     * @throws \Exception
622
+     */
623
+    public function testLoginName($loginName) {
624
+        if(!$this->checkRequirements(array('ldapHost',
625
+            'ldapPort',
626
+            'ldapBase',
627
+            'ldapLoginFilter',
628
+        ))) {
629
+            return false;
630
+        }
631
+
632
+        $cr = $this->access->connection->getConnectionResource();
633
+        if(!$this->ldap->isResource($cr)) {
634
+            throw new \Exception('connection error');
635
+        }
636
+
637
+        if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
638
+            === false) {
639
+            throw new \Exception('missing placeholder');
640
+        }
641
+
642
+        $users = $this->access->countUsersByLoginName($loginName);
643
+        if($this->ldap->errno($cr) !== 0) {
644
+            throw new \Exception($this->ldap->error($cr));
645
+        }
646
+        $filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
647
+        $this->result->addChange('ldap_test_loginname', $users);
648
+        $this->result->addChange('ldap_test_effective_filter', $filter);
649
+        return $this->result;
650
+    }
651
+
652
+    /**
653
+     * Tries to determine the port, requires given Host, User DN and Password
654
+     * @return WizardResult|false WizardResult on success, false otherwise
655
+     * @throws \Exception
656
+     */
657
+    public function guessPortAndTLS() {
658
+        if(!$this->checkRequirements(array('ldapHost',
659
+                                            ))) {
660
+            return false;
661
+        }
662
+        $this->checkHost();
663
+        $portSettings = $this->getPortSettingsToTry();
664
+
665
+        if(!is_array($portSettings)) {
666
+            throw new \Exception(print_r($portSettings, true));
667
+        }
668
+
669
+        //proceed from the best configuration and return on first success
670
+        foreach($portSettings as $setting) {
671
+            $p = $setting['port'];
672
+            $t = $setting['tls'];
673
+            \OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
674
+            //connectAndBind may throw Exception, it needs to be catched by the
675
+            //callee of this method
676
+
677
+            try {
678
+                $settingsFound = $this->connectAndBind($p, $t);
679
+            } catch (\Exception $e) {
680
+                // any reply other than -1 (= cannot connect) is already okay,
681
+                // because then we found the server
682
+                // unavailable startTLS returns -11
683
+                if($e->getCode() > 0) {
684
+                    $settingsFound = true;
685
+                } else {
686
+                    throw $e;
687
+                }
688
+            }
689
+
690
+            if ($settingsFound === true) {
691
+                $config = array(
692
+                    'ldapPort' => $p,
693
+                    'ldapTLS' => intval($t)
694
+                );
695
+                $this->configuration->setConfiguration($config);
696
+                \OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
697
+                $this->result->addChange('ldap_port', $p);
698
+                return $this->result;
699
+            }
700
+        }
701
+
702
+        //custom port, undetected (we do not brute force)
703
+        return false;
704
+    }
705
+
706
+    /**
707
+     * tries to determine a base dn from User DN or LDAP Host
708
+     * @return WizardResult|false WizardResult on success, false otherwise
709
+     */
710
+    public function guessBaseDN() {
711
+        if(!$this->checkRequirements(array('ldapHost',
712
+                                            'ldapPort',
713
+                                            ))) {
714
+            return false;
715
+        }
716
+
717
+        //check whether a DN is given in the agent name (99.9% of all cases)
718
+        $base = null;
719
+        $i = stripos($this->configuration->ldapAgentName, 'dc=');
720
+        if($i !== false) {
721
+            $base = substr($this->configuration->ldapAgentName, $i);
722
+            if($this->testBaseDN($base)) {
723
+                $this->applyFind('ldap_base', $base);
724
+                return $this->result;
725
+            }
726
+        }
727
+
728
+        //this did not help :(
729
+        //Let's see whether we can parse the Host URL and convert the domain to
730
+        //a base DN
731
+        $helper = new Helper(\OC::$server->getConfig());
732
+        $domain = $helper->getDomainFromURL($this->configuration->ldapHost);
733
+        if(!$domain) {
734
+            return false;
735
+        }
736
+
737
+        $dparts = explode('.', $domain);
738
+        while(count($dparts) > 0) {
739
+            $base2 = 'dc=' . implode(',dc=', $dparts);
740
+            if ($base !== $base2 && $this->testBaseDN($base2)) {
741
+                $this->applyFind('ldap_base', $base2);
742
+                return $this->result;
743
+            }
744
+            array_shift($dparts);
745
+        }
746
+
747
+        return false;
748
+    }
749
+
750
+    /**
751
+     * sets the found value for the configuration key in the WizardResult
752
+     * as well as in the Configuration instance
753
+     * @param string $key the configuration key
754
+     * @param string $value the (detected) value
755
+     *
756
+     */
757
+    private function applyFind($key, $value) {
758
+        $this->result->addChange($key, $value);
759
+        $this->configuration->setConfiguration(array($key => $value));
760
+    }
761
+
762
+    /**
763
+     * Checks, whether a port was entered in the Host configuration
764
+     * field. In this case the port will be stripped off, but also stored as
765
+     * setting.
766
+     */
767
+    private function checkHost() {
768
+        $host = $this->configuration->ldapHost;
769
+        $hostInfo = parse_url($host);
770
+
771
+        //removes Port from Host
772
+        if(is_array($hostInfo) && isset($hostInfo['port'])) {
773
+            $port = $hostInfo['port'];
774
+            $host = str_replace(':'.$port, '', $host);
775
+            $this->applyFind('ldap_host', $host);
776
+            $this->applyFind('ldap_port', $port);
777
+        }
778
+    }
779
+
780
+    /**
781
+     * tries to detect the group member association attribute which is
782
+     * one of 'uniqueMember', 'memberUid', 'member', 'gidNumber'
783
+     * @return string|false, string with the attribute name, false on error
784
+     * @throws \Exception
785
+     */
786
+    private function detectGroupMemberAssoc() {
787
+        $possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'gidNumber');
788
+        $filter = $this->configuration->ldapGroupFilter;
789
+        if(empty($filter)) {
790
+            return false;
791
+        }
792
+        $cr = $this->getConnection();
793
+        if(!$cr) {
794
+            throw new \Exception('Could not connect to LDAP');
795
+        }
796
+        $base = $this->configuration->ldapBase[0];
797
+        $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
798
+        if(!$this->ldap->isResource($rr)) {
799
+            return false;
800
+        }
801
+        $er = $this->ldap->firstEntry($cr, $rr);
802
+        while(is_resource($er)) {
803
+            $this->ldap->getDN($cr, $er);
804
+            $attrs = $this->ldap->getAttributes($cr, $er);
805
+            $result = array();
806
+            $possibleAttrsCount = count($possibleAttrs);
807
+            for($i = 0; $i < $possibleAttrsCount; $i++) {
808
+                if(isset($attrs[$possibleAttrs[$i]])) {
809
+                    $result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
810
+                }
811
+            }
812
+            if(!empty($result)) {
813
+                natsort($result);
814
+                return key($result);
815
+            }
816
+
817
+            $er = $this->ldap->nextEntry($cr, $er);
818
+        }
819
+
820
+        return false;
821
+    }
822
+
823
+    /**
824
+     * Checks whether for a given BaseDN results will be returned
825
+     * @param string $base the BaseDN to test
826
+     * @return bool true on success, false otherwise
827
+     * @throws \Exception
828
+     */
829
+    private function testBaseDN($base) {
830
+        $cr = $this->getConnection();
831
+        if(!$cr) {
832
+            throw new \Exception('Could not connect to LDAP');
833
+        }
834
+
835
+        //base is there, let's validate it. If we search for anything, we should
836
+        //get a result set > 0 on a proper base
837
+        $rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
838
+        if(!$this->ldap->isResource($rr)) {
839
+            $errorNo  = $this->ldap->errno($cr);
840
+            $errorMsg = $this->ldap->error($cr);
841
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
842
+                            ' Error '.$errorNo.': '.$errorMsg, \OCP\Util::INFO);
843
+            return false;
844
+        }
845
+        $entries = $this->ldap->countEntries($cr, $rr);
846
+        return ($entries !== false) && ($entries > 0);
847
+    }
848
+
849
+    /**
850
+     * Checks whether the server supports memberOf in LDAP Filter.
851
+     * Note: at least in OpenLDAP, availability of memberOf is dependent on
852
+     * a configured objectClass. I.e. not necessarily for all available groups
853
+     * memberOf does work.
854
+     *
855
+     * @return bool true if it does, false otherwise
856
+     * @throws \Exception
857
+     */
858
+    private function testMemberOf() {
859
+        $cr = $this->getConnection();
860
+        if(!$cr) {
861
+            throw new \Exception('Could not connect to LDAP');
862
+        }
863
+        $result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
864
+        if(is_int($result) &&  $result > 0) {
865
+            return true;
866
+        }
867
+        return false;
868
+    }
869
+
870
+    /**
871
+     * creates an LDAP Filter from given configuration
872
+     * @param integer $filterType int, for which use case the filter shall be created
873
+     * can be any of self::LFILTER_USER_LIST, self::LFILTER_LOGIN or
874
+     * self::LFILTER_GROUP_LIST
875
+     * @return string|false string with the filter on success, false otherwise
876
+     * @throws \Exception
877
+     */
878
+    private function composeLdapFilter($filterType) {
879
+        $filter = '';
880
+        $parts = 0;
881
+        switch ($filterType) {
882
+            case self::LFILTER_USER_LIST:
883
+                $objcs = $this->configuration->ldapUserFilterObjectclass;
884
+                //glue objectclasses
885
+                if(is_array($objcs) && count($objcs) > 0) {
886
+                    $filter .= '(|';
887
+                    foreach($objcs as $objc) {
888
+                        $filter .= '(objectclass=' . $objc . ')';
889
+                    }
890
+                    $filter .= ')';
891
+                    $parts++;
892
+                }
893
+                //glue group memberships
894
+                if($this->configuration->hasMemberOfFilterSupport) {
895
+                    $cns = $this->configuration->ldapUserFilterGroups;
896
+                    if(is_array($cns) && count($cns) > 0) {
897
+                        $filter .= '(|';
898
+                        $cr = $this->getConnection();
899
+                        if(!$cr) {
900
+                            throw new \Exception('Could not connect to LDAP');
901
+                        }
902
+                        $base = $this->configuration->ldapBase[0];
903
+                        foreach($cns as $cn) {
904
+                            $rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
905
+                            if(!$this->ldap->isResource($rr)) {
906
+                                continue;
907
+                            }
908
+                            $er = $this->ldap->firstEntry($cr, $rr);
909
+                            $attrs = $this->ldap->getAttributes($cr, $er);
910
+                            $dn = $this->ldap->getDN($cr, $er);
911
+                            if ($dn === false || $dn === '') {
912
+                                continue;
913
+                            }
914
+                            $filterPart = '(memberof=' . $dn . ')';
915
+                            if(isset($attrs['primaryGroupToken'])) {
916
+                                $pgt = $attrs['primaryGroupToken'][0];
917
+                                $primaryFilterPart = '(primaryGroupID=' . $pgt .')';
918
+                                $filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
919
+                            }
920
+                            $filter .= $filterPart;
921
+                        }
922
+                        $filter .= ')';
923
+                    }
924
+                    $parts++;
925
+                }
926
+                //wrap parts in AND condition
927
+                if($parts > 1) {
928
+                    $filter = '(&' . $filter . ')';
929
+                }
930
+                if ($filter === '') {
931
+                    $filter = '(objectclass=*)';
932
+                }
933
+                break;
934
+
935
+            case self::LFILTER_GROUP_LIST:
936
+                $objcs = $this->configuration->ldapGroupFilterObjectclass;
937
+                //glue objectclasses
938
+                if(is_array($objcs) && count($objcs) > 0) {
939
+                    $filter .= '(|';
940
+                    foreach($objcs as $objc) {
941
+                        $filter .= '(objectclass=' . $objc . ')';
942
+                    }
943
+                    $filter .= ')';
944
+                    $parts++;
945
+                }
946
+                //glue group memberships
947
+                $cns = $this->configuration->ldapGroupFilterGroups;
948
+                if(is_array($cns) && count($cns) > 0) {
949
+                    $filter .= '(|';
950
+                    foreach($cns as $cn) {
951
+                        $filter .= '(cn=' . $cn . ')';
952
+                    }
953
+                    $filter .= ')';
954
+                }
955
+                $parts++;
956
+                //wrap parts in AND condition
957
+                if($parts > 1) {
958
+                    $filter = '(&' . $filter . ')';
959
+                }
960
+                break;
961
+
962
+            case self::LFILTER_LOGIN:
963
+                $ulf = $this->configuration->ldapUserFilter;
964
+                $loginpart = '=%uid';
965
+                $filterUsername = '';
966
+                $userAttributes = $this->getUserAttributes();
967
+                $userAttributes = array_change_key_case(array_flip($userAttributes));
968
+                $parts = 0;
969
+
970
+                if($this->configuration->ldapLoginFilterUsername === '1') {
971
+                    $attr = '';
972
+                    if(isset($userAttributes['uid'])) {
973
+                        $attr = 'uid';
974
+                    } else if(isset($userAttributes['samaccountname'])) {
975
+                        $attr = 'samaccountname';
976
+                    } else if(isset($userAttributes['cn'])) {
977
+                        //fallback
978
+                        $attr = 'cn';
979
+                    }
980
+                    if ($attr !== '') {
981
+                        $filterUsername = '(' . $attr . $loginpart . ')';
982
+                        $parts++;
983
+                    }
984
+                }
985
+
986
+                $filterEmail = '';
987
+                if($this->configuration->ldapLoginFilterEmail === '1') {
988
+                    $filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
989
+                    $parts++;
990
+                }
991
+
992
+                $filterAttributes = '';
993
+                $attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
994
+                if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
995
+                    $filterAttributes = '(|';
996
+                    foreach($attrsToFilter as $attribute) {
997
+                        $filterAttributes .= '(' . $attribute . $loginpart . ')';
998
+                    }
999
+                    $filterAttributes .= ')';
1000
+                    $parts++;
1001
+                }
1002
+
1003
+                $filterLogin = '';
1004
+                if($parts > 1) {
1005
+                    $filterLogin = '(|';
1006
+                }
1007
+                $filterLogin .= $filterUsername;
1008
+                $filterLogin .= $filterEmail;
1009
+                $filterLogin .= $filterAttributes;
1010
+                if($parts > 1) {
1011
+                    $filterLogin .= ')';
1012
+                }
1013
+
1014
+                $filter = '(&'.$ulf.$filterLogin.')';
1015
+                break;
1016
+        }
1017
+
1018
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Final filter '.$filter, \OCP\Util::DEBUG);
1019
+
1020
+        return $filter;
1021
+    }
1022
+
1023
+    /**
1024
+     * Connects and Binds to an LDAP Server
1025
+     *
1026
+     * @param int $port the port to connect with
1027
+     * @param bool $tls whether startTLS is to be used
1028
+     * @return bool
1029
+     * @throws \Exception
1030
+     */
1031
+    private function connectAndBind($port, $tls) {
1032
+        //connect, does not really trigger any server communication
1033
+        $host = $this->configuration->ldapHost;
1034
+        $hostInfo = parse_url($host);
1035
+        if(!$hostInfo) {
1036
+            throw new \Exception(self::$l->t('Invalid Host'));
1037
+        }
1038
+        \OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039
+        $cr = $this->ldap->connect($host, $port);
1040
+        if(!is_resource($cr)) {
1041
+            throw new \Exception(self::$l->t('Invalid Host'));
1042
+        }
1043
+
1044
+        //set LDAP options
1045
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1046
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1047
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1048
+
1049
+        try {
1050
+            if($tls) {
1051
+                $isTlsWorking = @$this->ldap->startTls($cr);
1052
+                if(!$isTlsWorking) {
1053
+                    return false;
1054
+                }
1055
+            }
1056
+
1057
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Attemping to Bind ', \OCP\Util::DEBUG);
1058
+            //interesting part: do the bind!
1059
+            $login = $this->ldap->bind($cr,
1060
+                $this->configuration->ldapAgentName,
1061
+                $this->configuration->ldapAgentPassword
1062
+            );
1063
+            $errNo = $this->ldap->errno($cr);
1064
+            $error = ldap_error($cr);
1065
+            $this->ldap->unbind($cr);
1066
+        } catch(ServerNotAvailableException $e) {
1067
+            return false;
1068
+        }
1069
+
1070
+        if($login === true) {
1071
+            $this->ldap->unbind($cr);
1072
+            \OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1073
+            return true;
1074
+        }
1075
+
1076
+        if($errNo === -1) {
1077
+            //host, port or TLS wrong
1078
+            return false;
1079
+        }
1080
+        throw new \Exception($error, $errNo);
1081
+    }
1082
+
1083
+    /**
1084
+     * checks whether a valid combination of agent and password has been
1085
+     * provided (either two values or nothing for anonymous connect)
1086
+     * @return bool, true if everything is fine, false otherwise
1087
+     */
1088
+    private function checkAgentRequirements() {
1089
+        $agent = $this->configuration->ldapAgentName;
1090
+        $pwd = $this->configuration->ldapAgentPassword;
1091
+
1092
+        return
1093
+            ($agent !== '' && $pwd !== '')
1094
+            ||  ($agent === '' && $pwd === '')
1095
+        ;
1096
+    }
1097
+
1098
+    /**
1099
+     * @param array $reqs
1100
+     * @return bool
1101
+     */
1102
+    private function checkRequirements($reqs) {
1103
+        $this->checkAgentRequirements();
1104
+        foreach($reqs as $option) {
1105
+            $value = $this->configuration->$option;
1106
+            if(empty($value)) {
1107
+                return false;
1108
+            }
1109
+        }
1110
+        return true;
1111
+    }
1112
+
1113
+    /**
1114
+     * does a cumulativeSearch on LDAP to get different values of a
1115
+     * specified attribute
1116
+     * @param string[] $filters array, the filters that shall be used in the search
1117
+     * @param string $attr the attribute of which a list of values shall be returned
1118
+     * @param int $dnReadLimit the amount of how many DNs should be analyzed.
1119
+     * The lower, the faster
1120
+     * @param string $maxF string. if not null, this variable will have the filter that
1121
+     * yields most result entries
1122
+     * @return array|false an array with the values on success, false otherwise
1123
+     */
1124
+    public function cumulativeSearchOnAttribute($filters, $attr, $dnReadLimit = 3, &$maxF = null) {
1125
+        $dnRead = array();
1126
+        $foundItems = array();
1127
+        $maxEntries = 0;
1128
+        if(!is_array($this->configuration->ldapBase)
1129
+           || !isset($this->configuration->ldapBase[0])) {
1130
+            return false;
1131
+        }
1132
+        $base = $this->configuration->ldapBase[0];
1133
+        $cr = $this->getConnection();
1134
+        if(!$this->ldap->isResource($cr)) {
1135
+            return false;
1136
+        }
1137
+        $lastFilter = null;
1138
+        if(isset($filters[count($filters)-1])) {
1139
+            $lastFilter = $filters[count($filters)-1];
1140
+        }
1141
+        foreach($filters as $filter) {
1142
+            if($lastFilter === $filter && count($foundItems) > 0) {
1143
+                //skip when the filter is a wildcard and results were found
1144
+                continue;
1145
+            }
1146
+            // 20k limit for performance and reason
1147
+            $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1148
+            if(!$this->ldap->isResource($rr)) {
1149
+                continue;
1150
+            }
1151
+            $entries = $this->ldap->countEntries($cr, $rr);
1152
+            $getEntryFunc = 'firstEntry';
1153
+            if(($entries !== false) && ($entries > 0)) {
1154
+                if(!is_null($maxF) && $entries > $maxEntries) {
1155
+                    $maxEntries = $entries;
1156
+                    $maxF = $filter;
1157
+                }
1158
+                $dnReadCount = 0;
1159
+                do {
1160
+                    $entry = $this->ldap->$getEntryFunc($cr, $rr);
1161
+                    $getEntryFunc = 'nextEntry';
1162
+                    if(!$this->ldap->isResource($entry)) {
1163
+                        continue 2;
1164
+                    }
1165
+                    $rr = $entry; //will be expected by nextEntry next round
1166
+                    $attributes = $this->ldap->getAttributes($cr, $entry);
1167
+                    $dn = $this->ldap->getDN($cr, $entry);
1168
+                    if($dn === false || in_array($dn, $dnRead)) {
1169
+                        continue;
1170
+                    }
1171
+                    $newItems = array();
1172
+                    $state = $this->getAttributeValuesFromEntry($attributes,
1173
+                                                                $attr,
1174
+                                                                $newItems);
1175
+                    $dnReadCount++;
1176
+                    $foundItems = array_merge($foundItems, $newItems);
1177
+                    $this->resultCache[$dn][$attr] = $newItems;
1178
+                    $dnRead[] = $dn;
1179
+                } while(($state === self::LRESULT_PROCESSED_SKIP
1180
+                        || $this->ldap->isResource($entry))
1181
+                        && ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1182
+            }
1183
+        }
1184
+
1185
+        return array_unique($foundItems);
1186
+    }
1187
+
1188
+    /**
1189
+     * determines if and which $attr are available on the LDAP server
1190
+     * @param string[] $objectclasses the objectclasses to use as search filter
1191
+     * @param string $attr the attribute to look for
1192
+     * @param string $dbkey the dbkey of the setting the feature is connected to
1193
+     * @param string $confkey the confkey counterpart for the $dbkey as used in the
1194
+     * Configuration class
1195
+     * @param bool $po whether the objectClass with most result entries
1196
+     * shall be pre-selected via the result
1197
+     * @return array|false list of found items.
1198
+     * @throws \Exception
1199
+     */
1200
+    private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1201
+        $cr = $this->getConnection();
1202
+        if(!$cr) {
1203
+            throw new \Exception('Could not connect to LDAP');
1204
+        }
1205
+        $p = 'objectclass=';
1206
+        foreach($objectclasses as $key => $value) {
1207
+            $objectclasses[$key] = $p.$value;
1208
+        }
1209
+        $maxEntryObjC = '';
1210
+
1211
+        //how deep to dig?
1212
+        //When looking for objectclasses, testing few entries is sufficient,
1213
+        $dig = 3;
1214
+
1215
+        $availableFeatures =
1216
+            $this->cumulativeSearchOnAttribute($objectclasses, $attr,
1217
+                                                $dig, $maxEntryObjC);
1218
+        if(is_array($availableFeatures)
1219
+           && count($availableFeatures) > 0) {
1220
+            natcasesort($availableFeatures);
1221
+            //natcasesort keeps indices, but we must get rid of them for proper
1222
+            //sorting in the web UI. Therefore: array_values
1223
+            $this->result->addOptions($dbkey, array_values($availableFeatures));
1224
+        } else {
1225
+            throw new \Exception(self::$l->t('Could not find the desired feature'));
1226
+        }
1227
+
1228
+        $setFeatures = $this->configuration->$confkey;
1229
+        if(is_array($setFeatures) && !empty($setFeatures)) {
1230
+            //something is already configured? pre-select it.
1231
+            $this->result->addChange($dbkey, $setFeatures);
1232
+        } else if ($po && $maxEntryObjC !== '') {
1233
+            //pre-select objectclass with most result entries
1234
+            $maxEntryObjC = str_replace($p, '', $maxEntryObjC);
1235
+            $this->applyFind($dbkey, $maxEntryObjC);
1236
+            $this->result->addChange($dbkey, $maxEntryObjC);
1237
+        }
1238
+
1239
+        return $availableFeatures;
1240
+    }
1241
+
1242
+    /**
1243
+     * appends a list of values fr
1244
+     * @param resource $result the return value from ldap_get_attributes
1245
+     * @param string $attribute the attribute values to look for
1246
+     * @param array &$known new values will be appended here
1247
+     * @return int, state on of the class constants LRESULT_PROCESSED_OK,
1248
+     * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1249
+     */
1250
+    private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1251
+        if(!is_array($result)
1252
+           || !isset($result['count'])
1253
+           || !$result['count'] > 0) {
1254
+            return self::LRESULT_PROCESSED_INVALID;
1255
+        }
1256
+
1257
+        // strtolower on all keys for proper comparison
1258
+        $result = \OCP\Util::mb_array_change_key_case($result);
1259
+        $attribute = strtolower($attribute);
1260
+        if(isset($result[$attribute])) {
1261
+            foreach($result[$attribute] as $key => $val) {
1262
+                if($key === 'count') {
1263
+                    continue;
1264
+                }
1265
+                if(!in_array($val, $known)) {
1266
+                    $known[] = $val;
1267
+                }
1268
+            }
1269
+            return self::LRESULT_PROCESSED_OK;
1270
+        } else {
1271
+            return self::LRESULT_PROCESSED_SKIP;
1272
+        }
1273
+    }
1274
+
1275
+    /**
1276
+     * @return bool|mixed
1277
+     */
1278
+    private function getConnection() {
1279
+        if(!is_null($this->cr)) {
1280
+            return $this->cr;
1281
+        }
1282
+
1283
+        $cr = $this->ldap->connect(
1284
+            $this->configuration->ldapHost,
1285
+            $this->configuration->ldapPort
1286
+        );
1287
+
1288
+        $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1289
+        $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1290
+        $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1291
+        if($this->configuration->ldapTLS === 1) {
1292
+            $this->ldap->startTls($cr);
1293
+        }
1294
+
1295
+        $lo = @$this->ldap->bind($cr,
1296
+                                    $this->configuration->ldapAgentName,
1297
+                                    $this->configuration->ldapAgentPassword);
1298
+        if($lo === true) {
1299
+            $this->$cr = $cr;
1300
+            return $cr;
1301
+        }
1302
+
1303
+        return false;
1304
+    }
1305
+
1306
+    /**
1307
+     * @return array
1308
+     */
1309
+    private function getDefaultLdapPortSettings() {
1310
+        static $settings = array(
1311
+                                array('port' => 7636, 'tls' => false),
1312
+                                array('port' =>  636, 'tls' => false),
1313
+                                array('port' => 7389, 'tls' => true),
1314
+                                array('port' =>  389, 'tls' => true),
1315
+                                array('port' => 7389, 'tls' => false),
1316
+                                array('port' =>  389, 'tls' => false),
1317
+                            );
1318
+        return $settings;
1319
+    }
1320
+
1321
+    /**
1322
+     * @return array
1323
+     */
1324
+    private function getPortSettingsToTry() {
1325
+        //389 ← LDAP / Unencrypted or StartTLS
1326
+        //636 ← LDAPS / SSL
1327
+        //7xxx ← UCS. need to be checked first, because both ports may be open
1328
+        $host = $this->configuration->ldapHost;
1329
+        $port = intval($this->configuration->ldapPort);
1330
+        $portSettings = array();
1331
+
1332
+        //In case the port is already provided, we will check this first
1333
+        if($port > 0) {
1334
+            $hostInfo = parse_url($host);
1335
+            if(!(is_array($hostInfo)
1336
+                && isset($hostInfo['scheme'])
1337
+                && stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1338
+                $portSettings[] = array('port' => $port, 'tls' => true);
1339
+            }
1340
+            $portSettings[] =array('port' => $port, 'tls' => false);
1341
+        }
1342
+
1343
+        //default ports
1344
+        $portSettings = array_merge($portSettings,
1345
+                                    $this->getDefaultLdapPortSettings());
1346
+
1347
+        return $portSettings;
1348
+    }
1349 1349
 
1350 1350
 
1351 1351
 }
Please login to merge, or discard this patch.
Spacing   +152 added lines, -152 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 	public function __construct(Configuration $configuration, ILDAPWrapper $ldap, Access $access) {
72 72
 		parent::__construct($ldap);
73 73
 		$this->configuration = $configuration;
74
-		if(is_null(Wizard::$l)) {
74
+		if (is_null(Wizard::$l)) {
75 75
 			Wizard::$l = \OC::$server->getL10N('user_ldap');
76 76
 		}
77 77
 		$this->access = $access;
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
 	}
80 80
 
81 81
 	public function  __destruct() {
82
-		if($this->result->hasChanges()) {
82
+		if ($this->result->hasChanges()) {
83 83
 			$this->configuration->saveConfiguration();
84 84
 		}
85 85
 	}
@@ -94,18 +94,18 @@  discard block
 block discarded – undo
94 94
 	 */
95 95
 	public function countEntries($filter, $type) {
96 96
 		$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
97
-		if($type === 'users') {
97
+		if ($type === 'users') {
98 98
 			$reqs[] = 'ldapUserFilter';
99 99
 		}
100
-		if(!$this->checkRequirements($reqs)) {
100
+		if (!$this->checkRequirements($reqs)) {
101 101
 			throw new \Exception('Requirements not met', 400);
102 102
 		}
103 103
 
104 104
 		$attr = array('dn'); // default
105 105
 		$limit = 1001;
106
-		if($type === 'groups') {
107
-			$result =  $this->access->countGroups($filter, $attr, $limit);
108
-		} else if($type === 'users') {
106
+		if ($type === 'groups') {
107
+			$result = $this->access->countGroups($filter, $attr, $limit);
108
+		} else if ($type === 'users') {
109 109
 			$result = $this->access->countUsers($filter, $attr, $limit);
110 110
 		} else if ($type === 'objects') {
111 111
 			$result = $this->access->countObjects($limit);
@@ -125,7 +125,7 @@  discard block
 block discarded – undo
125 125
 	 */
126 126
 	private function formatCountResult($count) {
127 127
 		$formatted = ($count !== false) ? $count : 0;
128
-		if($formatted > 1000) {
128
+		if ($formatted > 1000) {
129 129
 			$formatted = '> 1000';
130 130
 		}
131 131
 		return $formatted;
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
 	public function countGroups() {
135 135
 		$filter = $this->configuration->ldapGroupFilter;
136 136
 
137
-		if(empty($filter)) {
137
+		if (empty($filter)) {
138 138
 			$output = self::$l->n('%s group found', '%s groups found', 0, array(0));
139 139
 			$this->result->addChange('ldap_group_count', $output);
140 140
 			return $this->result;
@@ -144,7 +144,7 @@  discard block
 block discarded – undo
144 144
 			$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
145 145
 		} catch (\Exception $e) {
146 146
 			//400 can be ignored, 500 is forwarded
147
-			if($e->getCode() === 500) {
147
+			if ($e->getCode() === 500) {
148 148
 				throw $e;
149 149
 			}
150 150
 			return false;
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
 	public function countInBaseDN() {
177 177
 		// we don't need to provide a filter in this case
178 178
 		$total = $this->countEntries(null, 'objects');
179
-		if($total === false) {
179
+		if ($total === false) {
180 180
 			throw new \Exception('invalid results received');
181 181
 		}
182 182
 		$this->result->addChange('ldap_test_base', $total);
@@ -190,7 +190,7 @@  discard block
 block discarded – undo
190 190
 	 * @return int|bool
191 191
 	 */
192 192
 	public function countUsersWithAttribute($attr, $existsCheck = false) {
193
-		if(!$this->checkRequirements(array('ldapHost',
193
+		if (!$this->checkRequirements(array('ldapHost',
194 194
 										   'ldapPort',
195 195
 										   'ldapBase',
196 196
 										   'ldapUserFilter',
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
 
201 201
 		$filter = $this->access->combineFilterWithAnd(array(
202 202
 			$this->configuration->ldapUserFilter,
203
-			$attr . '=*'
203
+			$attr.'=*'
204 204
 		));
205 205
 
206 206
 		$limit = ($existsCheck === false) ? null : 1;
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
 	 * @throws \Exception
216 216
 	 */
217 217
 	public function detectUserDisplayNameAttribute() {
218
-		if(!$this->checkRequirements(array('ldapHost',
218
+		if (!$this->checkRequirements(array('ldapHost',
219 219
 										'ldapPort',
220 220
 										'ldapBase',
221 221
 										'ldapUserFilter',
@@ -227,8 +227,8 @@  discard block
 block discarded – undo
227 227
 		if ($attr !== '' && $attr !== 'displayName') {
228 228
 			// most likely not the default value with upper case N,
229 229
 			// verify it still produces a result
230
-			$count = (int)$this->countUsersWithAttribute($attr, true);
231
-			if($count > 0) {
230
+			$count = (int) $this->countUsersWithAttribute($attr, true);
231
+			if ($count > 0) {
232 232
 				//no change, but we sent it back to make sure the user interface
233 233
 				//is still correct, even if the ajax call was cancelled meanwhile
234 234
 				$this->result->addChange('ldap_display_name', $attr);
@@ -239,9 +239,9 @@  discard block
 block discarded – undo
239 239
 		// first attribute that has at least one result wins
240 240
 		$displayNameAttrs = array('displayname', 'cn');
241 241
 		foreach ($displayNameAttrs as $attr) {
242
-			$count = (int)$this->countUsersWithAttribute($attr, true);
242
+			$count = (int) $this->countUsersWithAttribute($attr, true);
243 243
 
244
-			if($count > 0) {
244
+			if ($count > 0) {
245 245
 				$this->applyFind('ldap_display_name', $attr);
246 246
 				return $this->result;
247 247
 			}
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
 	 * @return WizardResult|bool
258 258
 	 */
259 259
 	public function detectEmailAttribute() {
260
-		if(!$this->checkRequirements(array('ldapHost',
260
+		if (!$this->checkRequirements(array('ldapHost',
261 261
 										   'ldapPort',
262 262
 										   'ldapBase',
263 263
 										   'ldapUserFilter',
@@ -267,8 +267,8 @@  discard block
 block discarded – undo
267 267
 
268 268
 		$attr = $this->configuration->ldapEmailAttribute;
269 269
 		if ($attr !== '') {
270
-			$count = (int)$this->countUsersWithAttribute($attr, true);
271
-			if($count > 0) {
270
+			$count = (int) $this->countUsersWithAttribute($attr, true);
271
+			if ($count > 0) {
272 272
 				return false;
273 273
 			}
274 274
 			$writeLog = true;
@@ -279,19 +279,19 @@  discard block
 block discarded – undo
279 279
 		$emailAttributes = array('mail', 'mailPrimaryAddress');
280 280
 		$winner = '';
281 281
 		$maxUsers = 0;
282
-		foreach($emailAttributes as $attr) {
282
+		foreach ($emailAttributes as $attr) {
283 283
 			$count = $this->countUsersWithAttribute($attr);
284
-			if($count > $maxUsers) {
284
+			if ($count > $maxUsers) {
285 285
 				$maxUsers = $count;
286 286
 				$winner = $attr;
287 287
 			}
288 288
 		}
289 289
 
290
-		if($winner !== '') {
290
+		if ($winner !== '') {
291 291
 			$this->applyFind('ldap_email_attr', $winner);
292
-			if($writeLog) {
293
-				\OCP\Util::writeLog('user_ldap', 'The mail attribute has ' .
294
-					'automatically been reset, because the original value ' .
292
+			if ($writeLog) {
293
+				\OCP\Util::writeLog('user_ldap', 'The mail attribute has '.
294
+					'automatically been reset, because the original value '.
295 295
 					'did not return any results.', \OCP\Util::INFO);
296 296
 			}
297 297
 		}
@@ -304,7 +304,7 @@  discard block
 block discarded – undo
304 304
 	 * @throws \Exception
305 305
 	 */
306 306
 	public function determineAttributes() {
307
-		if(!$this->checkRequirements(array('ldapHost',
307
+		if (!$this->checkRequirements(array('ldapHost',
308 308
 										   'ldapPort',
309 309
 										   'ldapBase',
310 310
 										   'ldapUserFilter',
@@ -320,7 +320,7 @@  discard block
 block discarded – undo
320 320
 		$this->result->addOptions('ldap_loginfilter_attributes', $attributes);
321 321
 
322 322
 		$selected = $this->configuration->ldapLoginFilterAttributes;
323
-		if(is_array($selected) && !empty($selected)) {
323
+		if (is_array($selected) && !empty($selected)) {
324 324
 			$this->result->addChange('ldap_loginfilter_attributes', $selected);
325 325
 		}
326 326
 
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
 	 * @throws \Exception
334 334
 	 */
335 335
 	private function getUserAttributes() {
336
-		if(!$this->checkRequirements(array('ldapHost',
336
+		if (!$this->checkRequirements(array('ldapHost',
337 337
 										   'ldapPort',
338 338
 										   'ldapBase',
339 339
 										   'ldapUserFilter',
@@ -341,20 +341,20 @@  discard block
 block discarded – undo
341 341
 			return  false;
342 342
 		}
343 343
 		$cr = $this->getConnection();
344
-		if(!$cr) {
344
+		if (!$cr) {
345 345
 			throw new \Exception('Could not connect to LDAP');
346 346
 		}
347 347
 
348 348
 		$base = $this->configuration->ldapBase[0];
349 349
 		$filter = $this->configuration->ldapUserFilter;
350 350
 		$rr = $this->ldap->search($cr, $base, $filter, array(), 1, 1);
351
-		if(!$this->ldap->isResource($rr)) {
351
+		if (!$this->ldap->isResource($rr)) {
352 352
 			return false;
353 353
 		}
354 354
 		$er = $this->ldap->firstEntry($cr, $rr);
355 355
 		$attributes = $this->ldap->getAttributes($cr, $er);
356 356
 		$pureAttributes = array();
357
-		for($i = 0; $i < $attributes['count']; $i++) {
357
+		for ($i = 0; $i < $attributes['count']; $i++) {
358 358
 			$pureAttributes[] = $attributes[$i];
359 359
 		}
360 360
 
@@ -389,23 +389,23 @@  discard block
 block discarded – undo
389 389
 	 * @throws \Exception
390 390
 	 */
391 391
 	private function determineGroups($dbKey, $confKey, $testMemberOf = true) {
392
-		if(!$this->checkRequirements(array('ldapHost',
392
+		if (!$this->checkRequirements(array('ldapHost',
393 393
 										   'ldapPort',
394 394
 										   'ldapBase',
395 395
 										   ))) {
396 396
 			return  false;
397 397
 		}
398 398
 		$cr = $this->getConnection();
399
-		if(!$cr) {
399
+		if (!$cr) {
400 400
 			throw new \Exception('Could not connect to LDAP');
401 401
 		}
402 402
 
403 403
 		$this->fetchGroups($dbKey, $confKey);
404 404
 
405
-		if($testMemberOf) {
405
+		if ($testMemberOf) {
406 406
 			$this->configuration->hasMemberOfFilterSupport = $this->testMemberOf();
407 407
 			$this->result->markChange();
408
-			if(!$this->configuration->hasMemberOfFilterSupport) {
408
+			if (!$this->configuration->hasMemberOfFilterSupport) {
409 409
 				throw new \Exception('memberOf is not supported by the server');
410 410
 			}
411 411
 		}
@@ -425,7 +425,7 @@  discard block
 block discarded – undo
425 425
 		$obclasses = array('posixGroup', 'group', 'zimbraDistributionList', 'groupOfNames', 'groupOfUniqueNames');
426 426
 
427 427
 		$filterParts = array();
428
-		foreach($obclasses as $obclass) {
428
+		foreach ($obclasses as $obclass) {
429 429
 			$filterParts[] = 'objectclass='.$obclass;
430 430
 		}
431 431
 		//we filter for everything
@@ -442,8 +442,8 @@  discard block
 block discarded – undo
442 442
 			// we need to request dn additionally here, otherwise memberOf
443 443
 			// detection will fail later
444 444
 			$result = $this->access->searchGroups($filter, array('cn', 'dn'), $limit, $offset);
445
-			foreach($result as $item) {
446
-				if(!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
445
+			foreach ($result as $item) {
446
+				if (!isset($item['cn']) && !is_array($item['cn']) && !isset($item['cn'][0])) {
447 447
 					// just in case - no issue known
448 448
 					continue;
449 449
 				}
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
 			$offset += $limit;
454 454
 		} while ($this->access->hasMoreResults());
455 455
 
456
-		if(count($groupNames) > 0) {
456
+		if (count($groupNames) > 0) {
457 457
 			natsort($groupNames);
458 458
 			$this->result->addOptions($dbKey, array_values($groupNames));
459 459
 		} else {
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
 		}
462 462
 
463 463
 		$setFeatures = $this->configuration->$confKey;
464
-		if(is_array($setFeatures) && !empty($setFeatures)) {
464
+		if (is_array($setFeatures) && !empty($setFeatures)) {
465 465
 			//something is already configured? pre-select it.
466 466
 			$this->result->addChange($dbKey, $setFeatures);
467 467
 		}
@@ -469,14 +469,14 @@  discard block
 block discarded – undo
469 469
 	}
470 470
 
471 471
 	public function determineGroupMemberAssoc() {
472
-		if(!$this->checkRequirements(array('ldapHost',
472
+		if (!$this->checkRequirements(array('ldapHost',
473 473
 										   'ldapPort',
474 474
 										   'ldapGroupFilter',
475 475
 										   ))) {
476 476
 			return  false;
477 477
 		}
478 478
 		$attribute = $this->detectGroupMemberAssoc();
479
-		if($attribute === false) {
479
+		if ($attribute === false) {
480 480
 			return false;
481 481
 		}
482 482
 		$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
@@ -491,14 +491,14 @@  discard block
 block discarded – undo
491 491
 	 * @throws \Exception
492 492
 	 */
493 493
 	public function determineGroupObjectClasses() {
494
-		if(!$this->checkRequirements(array('ldapHost',
494
+		if (!$this->checkRequirements(array('ldapHost',
495 495
 										   'ldapPort',
496 496
 										   'ldapBase',
497 497
 										   ))) {
498 498
 			return  false;
499 499
 		}
500 500
 		$cr = $this->getConnection();
501
-		if(!$cr) {
501
+		if (!$cr) {
502 502
 			throw new \Exception('Could not connect to LDAP');
503 503
 		}
504 504
 
@@ -518,14 +518,14 @@  discard block
 block discarded – undo
518 518
 	 * @throws \Exception
519 519
 	 */
520 520
 	public function determineUserObjectClasses() {
521
-		if(!$this->checkRequirements(array('ldapHost',
521
+		if (!$this->checkRequirements(array('ldapHost',
522 522
 										   'ldapPort',
523 523
 										   'ldapBase',
524 524
 										   ))) {
525 525
 			return  false;
526 526
 		}
527 527
 		$cr = $this->getConnection();
528
-		if(!$cr) {
528
+		if (!$cr) {
529 529
 			throw new \Exception('Could not connect to LDAP');
530 530
 		}
531 531
 
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
 	 * @throws \Exception
549 549
 	 */
550 550
 	public function getGroupFilter() {
551
-		if(!$this->checkRequirements(array('ldapHost',
551
+		if (!$this->checkRequirements(array('ldapHost',
552 552
 										   'ldapPort',
553 553
 										   'ldapBase',
554 554
 										   ))) {
@@ -572,7 +572,7 @@  discard block
 block discarded – undo
572 572
 	 * @throws \Exception
573 573
 	 */
574 574
 	public function getUserListFilter() {
575
-		if(!$this->checkRequirements(array('ldapHost',
575
+		if (!$this->checkRequirements(array('ldapHost',
576 576
 										   'ldapPort',
577 577
 										   'ldapBase',
578 578
 										   ))) {
@@ -585,7 +585,7 @@  discard block
 block discarded – undo
585 585
 			$this->applyFind('ldap_display_name', $d['ldap_display_name']);
586 586
 		}
587 587
 		$filter = $this->composeLdapFilter(self::LFILTER_USER_LIST);
588
-		if(!$filter) {
588
+		if (!$filter) {
589 589
 			throw new \Exception('Cannot create filter');
590 590
 		}
591 591
 
@@ -598,7 +598,7 @@  discard block
 block discarded – undo
598 598
 	 * @throws \Exception
599 599
 	 */
600 600
 	public function getUserLoginFilter() {
601
-		if(!$this->checkRequirements(array('ldapHost',
601
+		if (!$this->checkRequirements(array('ldapHost',
602 602
 										   'ldapPort',
603 603
 										   'ldapBase',
604 604
 										   'ldapUserFilter',
@@ -607,7 +607,7 @@  discard block
 block discarded – undo
607 607
 		}
608 608
 
609 609
 		$filter = $this->composeLdapFilter(self::LFILTER_LOGIN);
610
-		if(!$filter) {
610
+		if (!$filter) {
611 611
 			throw new \Exception('Cannot create filter');
612 612
 		}
613 613
 
@@ -621,7 +621,7 @@  discard block
 block discarded – undo
621 621
 	 * @throws \Exception
622 622
 	 */
623 623
 	public function testLoginName($loginName) {
624
-		if(!$this->checkRequirements(array('ldapHost',
624
+		if (!$this->checkRequirements(array('ldapHost',
625 625
 			'ldapPort',
626 626
 			'ldapBase',
627 627
 			'ldapLoginFilter',
@@ -630,17 +630,17 @@  discard block
 block discarded – undo
630 630
 		}
631 631
 
632 632
 		$cr = $this->access->connection->getConnectionResource();
633
-		if(!$this->ldap->isResource($cr)) {
633
+		if (!$this->ldap->isResource($cr)) {
634 634
 			throw new \Exception('connection error');
635 635
 		}
636 636
 
637
-		if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
637
+		if (mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
638 638
 			=== false) {
639 639
 			throw new \Exception('missing placeholder');
640 640
 		}
641 641
 
642 642
 		$users = $this->access->countUsersByLoginName($loginName);
643
-		if($this->ldap->errno($cr) !== 0) {
643
+		if ($this->ldap->errno($cr) !== 0) {
644 644
 			throw new \Exception($this->ldap->error($cr));
645 645
 		}
646 646
 		$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
@@ -655,22 +655,22 @@  discard block
 block discarded – undo
655 655
 	 * @throws \Exception
656 656
 	 */
657 657
 	public function guessPortAndTLS() {
658
-		if(!$this->checkRequirements(array('ldapHost',
658
+		if (!$this->checkRequirements(array('ldapHost',
659 659
 										   ))) {
660 660
 			return false;
661 661
 		}
662 662
 		$this->checkHost();
663 663
 		$portSettings = $this->getPortSettingsToTry();
664 664
 
665
-		if(!is_array($portSettings)) {
665
+		if (!is_array($portSettings)) {
666 666
 			throw new \Exception(print_r($portSettings, true));
667 667
 		}
668 668
 
669 669
 		//proceed from the best configuration and return on first success
670
-		foreach($portSettings as $setting) {
670
+		foreach ($portSettings as $setting) {
671 671
 			$p = $setting['port'];
672 672
 			$t = $setting['tls'];
673
-			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '. $p . ', TLS '. $t, \OCP\Util::DEBUG);
673
+			\OCP\Util::writeLog('user_ldap', 'Wiz: trying port '.$p.', TLS '.$t, \OCP\Util::DEBUG);
674 674
 			//connectAndBind may throw Exception, it needs to be catched by the
675 675
 			//callee of this method
676 676
 
@@ -680,7 +680,7 @@  discard block
 block discarded – undo
680 680
 				// any reply other than -1 (= cannot connect) is already okay,
681 681
 				// because then we found the server
682 682
 				// unavailable startTLS returns -11
683
-				if($e->getCode() > 0) {
683
+				if ($e->getCode() > 0) {
684 684
 					$settingsFound = true;
685 685
 				} else {
686 686
 					throw $e;
@@ -693,7 +693,7 @@  discard block
 block discarded – undo
693 693
 					'ldapTLS' => intval($t)
694 694
 				);
695 695
 				$this->configuration->setConfiguration($config);
696
-				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port ' . $p, \OCP\Util::DEBUG);
696
+				\OCP\Util::writeLog('user_ldap', 'Wiz: detected Port '.$p, \OCP\Util::DEBUG);
697 697
 				$this->result->addChange('ldap_port', $p);
698 698
 				return $this->result;
699 699
 			}
@@ -708,7 +708,7 @@  discard block
 block discarded – undo
708 708
 	 * @return WizardResult|false WizardResult on success, false otherwise
709 709
 	 */
710 710
 	public function guessBaseDN() {
711
-		if(!$this->checkRequirements(array('ldapHost',
711
+		if (!$this->checkRequirements(array('ldapHost',
712 712
 										   'ldapPort',
713 713
 										   ))) {
714 714
 			return false;
@@ -717,9 +717,9 @@  discard block
 block discarded – undo
717 717
 		//check whether a DN is given in the agent name (99.9% of all cases)
718 718
 		$base = null;
719 719
 		$i = stripos($this->configuration->ldapAgentName, 'dc=');
720
-		if($i !== false) {
720
+		if ($i !== false) {
721 721
 			$base = substr($this->configuration->ldapAgentName, $i);
722
-			if($this->testBaseDN($base)) {
722
+			if ($this->testBaseDN($base)) {
723 723
 				$this->applyFind('ldap_base', $base);
724 724
 				return $this->result;
725 725
 			}
@@ -730,13 +730,13 @@  discard block
 block discarded – undo
730 730
 		//a base DN
731 731
 		$helper = new Helper(\OC::$server->getConfig());
732 732
 		$domain = $helper->getDomainFromURL($this->configuration->ldapHost);
733
-		if(!$domain) {
733
+		if (!$domain) {
734 734
 			return false;
735 735
 		}
736 736
 
737 737
 		$dparts = explode('.', $domain);
738
-		while(count($dparts) > 0) {
739
-			$base2 = 'dc=' . implode(',dc=', $dparts);
738
+		while (count($dparts) > 0) {
739
+			$base2 = 'dc='.implode(',dc=', $dparts);
740 740
 			if ($base !== $base2 && $this->testBaseDN($base2)) {
741 741
 				$this->applyFind('ldap_base', $base2);
742 742
 				return $this->result;
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
 		$hostInfo = parse_url($host);
770 770
 
771 771
 		//removes Port from Host
772
-		if(is_array($hostInfo) && isset($hostInfo['port'])) {
772
+		if (is_array($hostInfo) && isset($hostInfo['port'])) {
773 773
 			$port = $hostInfo['port'];
774 774
 			$host = str_replace(':'.$port, '', $host);
775 775
 			$this->applyFind('ldap_host', $host);
@@ -786,30 +786,30 @@  discard block
 block discarded – undo
786 786
 	private function detectGroupMemberAssoc() {
787 787
 		$possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'gidNumber');
788 788
 		$filter = $this->configuration->ldapGroupFilter;
789
-		if(empty($filter)) {
789
+		if (empty($filter)) {
790 790
 			return false;
791 791
 		}
792 792
 		$cr = $this->getConnection();
793
-		if(!$cr) {
793
+		if (!$cr) {
794 794
 			throw new \Exception('Could not connect to LDAP');
795 795
 		}
796 796
 		$base = $this->configuration->ldapBase[0];
797 797
 		$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
798
-		if(!$this->ldap->isResource($rr)) {
798
+		if (!$this->ldap->isResource($rr)) {
799 799
 			return false;
800 800
 		}
801 801
 		$er = $this->ldap->firstEntry($cr, $rr);
802
-		while(is_resource($er)) {
802
+		while (is_resource($er)) {
803 803
 			$this->ldap->getDN($cr, $er);
804 804
 			$attrs = $this->ldap->getAttributes($cr, $er);
805 805
 			$result = array();
806 806
 			$possibleAttrsCount = count($possibleAttrs);
807
-			for($i = 0; $i < $possibleAttrsCount; $i++) {
808
-				if(isset($attrs[$possibleAttrs[$i]])) {
807
+			for ($i = 0; $i < $possibleAttrsCount; $i++) {
808
+				if (isset($attrs[$possibleAttrs[$i]])) {
809 809
 					$result[$possibleAttrs[$i]] = $attrs[$possibleAttrs[$i]]['count'];
810 810
 				}
811 811
 			}
812
-			if(!empty($result)) {
812
+			if (!empty($result)) {
813 813
 				natsort($result);
814 814
 				return key($result);
815 815
 			}
@@ -828,14 +828,14 @@  discard block
 block discarded – undo
828 828
 	 */
829 829
 	private function testBaseDN($base) {
830 830
 		$cr = $this->getConnection();
831
-		if(!$cr) {
831
+		if (!$cr) {
832 832
 			throw new \Exception('Could not connect to LDAP');
833 833
 		}
834 834
 
835 835
 		//base is there, let's validate it. If we search for anything, we should
836 836
 		//get a result set > 0 on a proper base
837 837
 		$rr = $this->ldap->search($cr, $base, 'objectClass=*', array('dn'), 0, 1);
838
-		if(!$this->ldap->isResource($rr)) {
838
+		if (!$this->ldap->isResource($rr)) {
839 839
 			$errorNo  = $this->ldap->errno($cr);
840 840
 			$errorMsg = $this->ldap->error($cr);
841 841
 			\OCP\Util::writeLog('user_ldap', 'Wiz: Could not search base '.$base.
@@ -857,11 +857,11 @@  discard block
 block discarded – undo
857 857
 	 */
858 858
 	private function testMemberOf() {
859 859
 		$cr = $this->getConnection();
860
-		if(!$cr) {
860
+		if (!$cr) {
861 861
 			throw new \Exception('Could not connect to LDAP');
862 862
 		}
863 863
 		$result = $this->access->countUsers('memberOf=*', array('memberOf'), 1);
864
-		if(is_int($result) &&  $result > 0) {
864
+		if (is_int($result) && $result > 0) {
865 865
 			return true;
866 866
 		}
867 867
 		return false;
@@ -882,27 +882,27 @@  discard block
 block discarded – undo
882 882
 			case self::LFILTER_USER_LIST:
883 883
 				$objcs = $this->configuration->ldapUserFilterObjectclass;
884 884
 				//glue objectclasses
885
-				if(is_array($objcs) && count($objcs) > 0) {
885
+				if (is_array($objcs) && count($objcs) > 0) {
886 886
 					$filter .= '(|';
887
-					foreach($objcs as $objc) {
888
-						$filter .= '(objectclass=' . $objc . ')';
887
+					foreach ($objcs as $objc) {
888
+						$filter .= '(objectclass='.$objc.')';
889 889
 					}
890 890
 					$filter .= ')';
891 891
 					$parts++;
892 892
 				}
893 893
 				//glue group memberships
894
-				if($this->configuration->hasMemberOfFilterSupport) {
894
+				if ($this->configuration->hasMemberOfFilterSupport) {
895 895
 					$cns = $this->configuration->ldapUserFilterGroups;
896
-					if(is_array($cns) && count($cns) > 0) {
896
+					if (is_array($cns) && count($cns) > 0) {
897 897
 						$filter .= '(|';
898 898
 						$cr = $this->getConnection();
899
-						if(!$cr) {
899
+						if (!$cr) {
900 900
 							throw new \Exception('Could not connect to LDAP');
901 901
 						}
902 902
 						$base = $this->configuration->ldapBase[0];
903
-						foreach($cns as $cn) {
904
-							$rr = $this->ldap->search($cr, $base, 'cn=' . $cn, array('dn', 'primaryGroupToken'));
905
-							if(!$this->ldap->isResource($rr)) {
903
+						foreach ($cns as $cn) {
904
+							$rr = $this->ldap->search($cr, $base, 'cn='.$cn, array('dn', 'primaryGroupToken'));
905
+							if (!$this->ldap->isResource($rr)) {
906 906
 								continue;
907 907
 							}
908 908
 							$er = $this->ldap->firstEntry($cr, $rr);
@@ -911,11 +911,11 @@  discard block
 block discarded – undo
911 911
 							if ($dn === false || $dn === '') {
912 912
 								continue;
913 913
 							}
914
-							$filterPart = '(memberof=' . $dn . ')';
915
-							if(isset($attrs['primaryGroupToken'])) {
914
+							$filterPart = '(memberof='.$dn.')';
915
+							if (isset($attrs['primaryGroupToken'])) {
916 916
 								$pgt = $attrs['primaryGroupToken'][0];
917
-								$primaryFilterPart = '(primaryGroupID=' . $pgt .')';
918
-								$filterPart = '(|' . $filterPart . $primaryFilterPart . ')';
917
+								$primaryFilterPart = '(primaryGroupID='.$pgt.')';
918
+								$filterPart = '(|'.$filterPart.$primaryFilterPart.')';
919 919
 							}
920 920
 							$filter .= $filterPart;
921 921
 						}
@@ -924,8 +924,8 @@  discard block
 block discarded – undo
924 924
 					$parts++;
925 925
 				}
926 926
 				//wrap parts in AND condition
927
-				if($parts > 1) {
928
-					$filter = '(&' . $filter . ')';
927
+				if ($parts > 1) {
928
+					$filter = '(&'.$filter.')';
929 929
 				}
930 930
 				if ($filter === '') {
931 931
 					$filter = '(objectclass=*)';
@@ -935,27 +935,27 @@  discard block
 block discarded – undo
935 935
 			case self::LFILTER_GROUP_LIST:
936 936
 				$objcs = $this->configuration->ldapGroupFilterObjectclass;
937 937
 				//glue objectclasses
938
-				if(is_array($objcs) && count($objcs) > 0) {
938
+				if (is_array($objcs) && count($objcs) > 0) {
939 939
 					$filter .= '(|';
940
-					foreach($objcs as $objc) {
941
-						$filter .= '(objectclass=' . $objc . ')';
940
+					foreach ($objcs as $objc) {
941
+						$filter .= '(objectclass='.$objc.')';
942 942
 					}
943 943
 					$filter .= ')';
944 944
 					$parts++;
945 945
 				}
946 946
 				//glue group memberships
947 947
 				$cns = $this->configuration->ldapGroupFilterGroups;
948
-				if(is_array($cns) && count($cns) > 0) {
948
+				if (is_array($cns) && count($cns) > 0) {
949 949
 					$filter .= '(|';
950
-					foreach($cns as $cn) {
951
-						$filter .= '(cn=' . $cn . ')';
950
+					foreach ($cns as $cn) {
951
+						$filter .= '(cn='.$cn.')';
952 952
 					}
953 953
 					$filter .= ')';
954 954
 				}
955 955
 				$parts++;
956 956
 				//wrap parts in AND condition
957
-				if($parts > 1) {
958
-					$filter = '(&' . $filter . ')';
957
+				if ($parts > 1) {
958
+					$filter = '(&'.$filter.')';
959 959
 				}
960 960
 				break;
961 961
 
@@ -967,47 +967,47 @@  discard block
 block discarded – undo
967 967
 				$userAttributes = array_change_key_case(array_flip($userAttributes));
968 968
 				$parts = 0;
969 969
 
970
-				if($this->configuration->ldapLoginFilterUsername === '1') {
970
+				if ($this->configuration->ldapLoginFilterUsername === '1') {
971 971
 					$attr = '';
972
-					if(isset($userAttributes['uid'])) {
972
+					if (isset($userAttributes['uid'])) {
973 973
 						$attr = 'uid';
974
-					} else if(isset($userAttributes['samaccountname'])) {
974
+					} else if (isset($userAttributes['samaccountname'])) {
975 975
 						$attr = 'samaccountname';
976
-					} else if(isset($userAttributes['cn'])) {
976
+					} else if (isset($userAttributes['cn'])) {
977 977
 						//fallback
978 978
 						$attr = 'cn';
979 979
 					}
980 980
 					if ($attr !== '') {
981
-						$filterUsername = '(' . $attr . $loginpart . ')';
981
+						$filterUsername = '('.$attr.$loginpart.')';
982 982
 						$parts++;
983 983
 					}
984 984
 				}
985 985
 
986 986
 				$filterEmail = '';
987
-				if($this->configuration->ldapLoginFilterEmail === '1') {
987
+				if ($this->configuration->ldapLoginFilterEmail === '1') {
988 988
 					$filterEmail = '(|(mailPrimaryAddress=%uid)(mail=%uid))';
989 989
 					$parts++;
990 990
 				}
991 991
 
992 992
 				$filterAttributes = '';
993 993
 				$attrsToFilter = $this->configuration->ldapLoginFilterAttributes;
994
-				if(is_array($attrsToFilter) && count($attrsToFilter) > 0) {
994
+				if (is_array($attrsToFilter) && count($attrsToFilter) > 0) {
995 995
 					$filterAttributes = '(|';
996
-					foreach($attrsToFilter as $attribute) {
997
-						$filterAttributes .= '(' . $attribute . $loginpart . ')';
996
+					foreach ($attrsToFilter as $attribute) {
997
+						$filterAttributes .= '('.$attribute.$loginpart.')';
998 998
 					}
999 999
 					$filterAttributes .= ')';
1000 1000
 					$parts++;
1001 1001
 				}
1002 1002
 
1003 1003
 				$filterLogin = '';
1004
-				if($parts > 1) {
1004
+				if ($parts > 1) {
1005 1005
 					$filterLogin = '(|';
1006 1006
 				}
1007 1007
 				$filterLogin .= $filterUsername;
1008 1008
 				$filterLogin .= $filterEmail;
1009 1009
 				$filterLogin .= $filterAttributes;
1010
-				if($parts > 1) {
1010
+				if ($parts > 1) {
1011 1011
 					$filterLogin .= ')';
1012 1012
 				}
1013 1013
 
@@ -1032,12 +1032,12 @@  discard block
 block discarded – undo
1032 1032
 		//connect, does not really trigger any server communication
1033 1033
 		$host = $this->configuration->ldapHost;
1034 1034
 		$hostInfo = parse_url($host);
1035
-		if(!$hostInfo) {
1035
+		if (!$hostInfo) {
1036 1036
 			throw new \Exception(self::$l->t('Invalid Host'));
1037 1037
 		}
1038 1038
 		\OCP\Util::writeLog('user_ldap', 'Wiz: Attempting to connect ', \OCP\Util::DEBUG);
1039 1039
 		$cr = $this->ldap->connect($host, $port);
1040
-		if(!is_resource($cr)) {
1040
+		if (!is_resource($cr)) {
1041 1041
 			throw new \Exception(self::$l->t('Invalid Host'));
1042 1042
 		}
1043 1043
 
@@ -1047,9 +1047,9 @@  discard block
 block discarded – undo
1047 1047
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1048 1048
 
1049 1049
 		try {
1050
-			if($tls) {
1050
+			if ($tls) {
1051 1051
 				$isTlsWorking = @$this->ldap->startTls($cr);
1052
-				if(!$isTlsWorking) {
1052
+				if (!$isTlsWorking) {
1053 1053
 					return false;
1054 1054
 				}
1055 1055
 			}
@@ -1063,17 +1063,17 @@  discard block
 block discarded – undo
1063 1063
 			$errNo = $this->ldap->errno($cr);
1064 1064
 			$error = ldap_error($cr);
1065 1065
 			$this->ldap->unbind($cr);
1066
-		} catch(ServerNotAvailableException $e) {
1066
+		} catch (ServerNotAvailableException $e) {
1067 1067
 			return false;
1068 1068
 		}
1069 1069
 
1070
-		if($login === true) {
1070
+		if ($login === true) {
1071 1071
 			$this->ldap->unbind($cr);
1072
-			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '. $port . ' TLS ' . intval($tls), \OCP\Util::DEBUG);
1072
+			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '.$port.' TLS '.intval($tls), \OCP\Util::DEBUG);
1073 1073
 			return true;
1074 1074
 		}
1075 1075
 
1076
-		if($errNo === -1) {
1076
+		if ($errNo === -1) {
1077 1077
 			//host, port or TLS wrong
1078 1078
 			return false;
1079 1079
 		}
@@ -1101,9 +1101,9 @@  discard block
 block discarded – undo
1101 1101
 	 */
1102 1102
 	private function checkRequirements($reqs) {
1103 1103
 		$this->checkAgentRequirements();
1104
-		foreach($reqs as $option) {
1104
+		foreach ($reqs as $option) {
1105 1105
 			$value = $this->configuration->$option;
1106
-			if(empty($value)) {
1106
+			if (empty($value)) {
1107 1107
 				return false;
1108 1108
 			}
1109 1109
 		}
@@ -1125,33 +1125,33 @@  discard block
 block discarded – undo
1125 1125
 		$dnRead = array();
1126 1126
 		$foundItems = array();
1127 1127
 		$maxEntries = 0;
1128
-		if(!is_array($this->configuration->ldapBase)
1128
+		if (!is_array($this->configuration->ldapBase)
1129 1129
 		   || !isset($this->configuration->ldapBase[0])) {
1130 1130
 			return false;
1131 1131
 		}
1132 1132
 		$base = $this->configuration->ldapBase[0];
1133 1133
 		$cr = $this->getConnection();
1134
-		if(!$this->ldap->isResource($cr)) {
1134
+		if (!$this->ldap->isResource($cr)) {
1135 1135
 			return false;
1136 1136
 		}
1137 1137
 		$lastFilter = null;
1138
-		if(isset($filters[count($filters)-1])) {
1139
-			$lastFilter = $filters[count($filters)-1];
1138
+		if (isset($filters[count($filters) - 1])) {
1139
+			$lastFilter = $filters[count($filters) - 1];
1140 1140
 		}
1141
-		foreach($filters as $filter) {
1142
-			if($lastFilter === $filter && count($foundItems) > 0) {
1141
+		foreach ($filters as $filter) {
1142
+			if ($lastFilter === $filter && count($foundItems) > 0) {
1143 1143
 				//skip when the filter is a wildcard and results were found
1144 1144
 				continue;
1145 1145
 			}
1146 1146
 			// 20k limit for performance and reason
1147 1147
 			$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
1148
-			if(!$this->ldap->isResource($rr)) {
1148
+			if (!$this->ldap->isResource($rr)) {
1149 1149
 				continue;
1150 1150
 			}
1151 1151
 			$entries = $this->ldap->countEntries($cr, $rr);
1152 1152
 			$getEntryFunc = 'firstEntry';
1153
-			if(($entries !== false) && ($entries > 0)) {
1154
-				if(!is_null($maxF) && $entries > $maxEntries) {
1153
+			if (($entries !== false) && ($entries > 0)) {
1154
+				if (!is_null($maxF) && $entries > $maxEntries) {
1155 1155
 					$maxEntries = $entries;
1156 1156
 					$maxF = $filter;
1157 1157
 				}
@@ -1159,13 +1159,13 @@  discard block
 block discarded – undo
1159 1159
 				do {
1160 1160
 					$entry = $this->ldap->$getEntryFunc($cr, $rr);
1161 1161
 					$getEntryFunc = 'nextEntry';
1162
-					if(!$this->ldap->isResource($entry)) {
1162
+					if (!$this->ldap->isResource($entry)) {
1163 1163
 						continue 2;
1164 1164
 					}
1165 1165
 					$rr = $entry; //will be expected by nextEntry next round
1166 1166
 					$attributes = $this->ldap->getAttributes($cr, $entry);
1167 1167
 					$dn = $this->ldap->getDN($cr, $entry);
1168
-					if($dn === false || in_array($dn, $dnRead)) {
1168
+					if ($dn === false || in_array($dn, $dnRead)) {
1169 1169
 						continue;
1170 1170
 					}
1171 1171
 					$newItems = array();
@@ -1176,7 +1176,7 @@  discard block
 block discarded – undo
1176 1176
 					$foundItems = array_merge($foundItems, $newItems);
1177 1177
 					$this->resultCache[$dn][$attr] = $newItems;
1178 1178
 					$dnRead[] = $dn;
1179
-				} while(($state === self::LRESULT_PROCESSED_SKIP
1179
+				} while (($state === self::LRESULT_PROCESSED_SKIP
1180 1180
 						|| $this->ldap->isResource($entry))
1181 1181
 						&& ($dnReadLimit === 0 || $dnReadCount < $dnReadLimit));
1182 1182
 			}
@@ -1199,11 +1199,11 @@  discard block
 block discarded – undo
1199 1199
 	 */
1200 1200
 	private function determineFeature($objectclasses, $attr, $dbkey, $confkey, $po = false) {
1201 1201
 		$cr = $this->getConnection();
1202
-		if(!$cr) {
1202
+		if (!$cr) {
1203 1203
 			throw new \Exception('Could not connect to LDAP');
1204 1204
 		}
1205 1205
 		$p = 'objectclass=';
1206
-		foreach($objectclasses as $key => $value) {
1206
+		foreach ($objectclasses as $key => $value) {
1207 1207
 			$objectclasses[$key] = $p.$value;
1208 1208
 		}
1209 1209
 		$maxEntryObjC = '';
@@ -1215,7 +1215,7 @@  discard block
 block discarded – undo
1215 1215
 		$availableFeatures =
1216 1216
 			$this->cumulativeSearchOnAttribute($objectclasses, $attr,
1217 1217
 											   $dig, $maxEntryObjC);
1218
-		if(is_array($availableFeatures)
1218
+		if (is_array($availableFeatures)
1219 1219
 		   && count($availableFeatures) > 0) {
1220 1220
 			natcasesort($availableFeatures);
1221 1221
 			//natcasesort keeps indices, but we must get rid of them for proper
@@ -1226,7 +1226,7 @@  discard block
 block discarded – undo
1226 1226
 		}
1227 1227
 
1228 1228
 		$setFeatures = $this->configuration->$confkey;
1229
-		if(is_array($setFeatures) && !empty($setFeatures)) {
1229
+		if (is_array($setFeatures) && !empty($setFeatures)) {
1230 1230
 			//something is already configured? pre-select it.
1231 1231
 			$this->result->addChange($dbkey, $setFeatures);
1232 1232
 		} else if ($po && $maxEntryObjC !== '') {
@@ -1248,7 +1248,7 @@  discard block
 block discarded – undo
1248 1248
 	 * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
1249 1249
 	 */
1250 1250
 	private function getAttributeValuesFromEntry($result, $attribute, &$known) {
1251
-		if(!is_array($result)
1251
+		if (!is_array($result)
1252 1252
 		   || !isset($result['count'])
1253 1253
 		   || !$result['count'] > 0) {
1254 1254
 			return self::LRESULT_PROCESSED_INVALID;
@@ -1257,12 +1257,12 @@  discard block
 block discarded – undo
1257 1257
 		// strtolower on all keys for proper comparison
1258 1258
 		$result = \OCP\Util::mb_array_change_key_case($result);
1259 1259
 		$attribute = strtolower($attribute);
1260
-		if(isset($result[$attribute])) {
1261
-			foreach($result[$attribute] as $key => $val) {
1262
-				if($key === 'count') {
1260
+		if (isset($result[$attribute])) {
1261
+			foreach ($result[$attribute] as $key => $val) {
1262
+				if ($key === 'count') {
1263 1263
 					continue;
1264 1264
 				}
1265
-				if(!in_array($val, $known)) {
1265
+				if (!in_array($val, $known)) {
1266 1266
 					$known[] = $val;
1267 1267
 				}
1268 1268
 			}
@@ -1276,7 +1276,7 @@  discard block
 block discarded – undo
1276 1276
 	 * @return bool|mixed
1277 1277
 	 */
1278 1278
 	private function getConnection() {
1279
-		if(!is_null($this->cr)) {
1279
+		if (!is_null($this->cr)) {
1280 1280
 			return $this->cr;
1281 1281
 		}
1282 1282
 
@@ -1288,14 +1288,14 @@  discard block
 block discarded – undo
1288 1288
 		$this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
1289 1289
 		$this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0);
1290 1290
 		$this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT);
1291
-		if($this->configuration->ldapTLS === 1) {
1291
+		if ($this->configuration->ldapTLS === 1) {
1292 1292
 			$this->ldap->startTls($cr);
1293 1293
 		}
1294 1294
 
1295 1295
 		$lo = @$this->ldap->bind($cr,
1296 1296
 								 $this->configuration->ldapAgentName,
1297 1297
 								 $this->configuration->ldapAgentPassword);
1298
-		if($lo === true) {
1298
+		if ($lo === true) {
1299 1299
 			$this->$cr = $cr;
1300 1300
 			return $cr;
1301 1301
 		}
@@ -1330,14 +1330,14 @@  discard block
 block discarded – undo
1330 1330
 		$portSettings = array();
1331 1331
 
1332 1332
 		//In case the port is already provided, we will check this first
1333
-		if($port > 0) {
1333
+		if ($port > 0) {
1334 1334
 			$hostInfo = parse_url($host);
1335
-			if(!(is_array($hostInfo)
1335
+			if (!(is_array($hostInfo)
1336 1336
 				&& isset($hostInfo['scheme'])
1337 1337
 				&& stripos($hostInfo['scheme'], 'ldaps') !== false)) {
1338 1338
 				$portSettings[] = array('port' => $port, 'tls' => true);
1339 1339
 			}
1340
-			$portSettings[] =array('port' => $port, 'tls' => false);
1340
+			$portSettings[] = array('port' => $port, 'tls' => false);
1341 1341
 		}
1342 1342
 
1343 1343
 		//default ports
Please login to merge, or discard this patch.
apps/files_sharing/lib/SharedMount.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 *
111 111
 	 * @param string $newPath
112 112
 	 * @param \OCP\Share\IShare $share
113
-	 * @return bool
113
+	 * @return boolean|null
114 114
 	 */
115 115
 	private function updateFileTarget($newPath, &$share) {
116 116
 		$share->setTarget($newPath);
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 	 * @param string $path
127 127
 	 * @param View $view
128 128
 	 * @param SharedMount[] $mountpoints
129
-	 * @return mixed
129
+	 * @return string
130 130
 	 */
131 131
 	private function generateUniqueTarget($path, $view, array $mountpoints) {
132 132
 		$pathinfo = pathinfo($path);
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -65,14 +65,14 @@  discard block
 block discarded – undo
65 65
 	 */
66 66
 	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
67 67
 		$this->user = $arguments['user'];
68
-		$this->recipientView = new View('/' . $this->user . '/files');
68
+		$this->recipientView = new View('/'.$this->user.'/files');
69 69
 
70 70
 		$this->superShare = $arguments['superShare'];
71 71
 		$this->groupedShares = $arguments['groupedShares'];
72 72
 
73 73
 		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
74
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
75
-		$arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
74
+		$absMountPoint = '/'.$this->user.'/files'.$newMountPoint;
75
+		$arguments['ownerView'] = new View('/'.$this->superShare->getShareOwner().'/files');
76 76
 		parent::__construct($storage, $absMountPoint, $arguments, $loader);
77 77
 	}
78 78
 
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
 		}
94 94
 
95 95
 		$newMountPoint = $this->generateUniqueTarget(
96
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
96
+			\OC\Files\Filesystem::normalizePath($parent.'/'.$mountPoint),
97 97
 			$this->recipientView,
98 98
 			$mountpoints
99 99
 		);
@@ -130,12 +130,12 @@  discard block
 block discarded – undo
130 130
 	 */
131 131
 	private function generateUniqueTarget($path, $view, array $mountpoints) {
132 132
 		$pathinfo = pathinfo($path);
133
-		$ext = (isset($pathinfo['extension'])) ? '.' . $pathinfo['extension'] : '';
133
+		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
134 134
 		$name = $pathinfo['filename'];
135 135
 		$dir = $pathinfo['dirname'];
136 136
 
137 137
 		// Helper function to find existing mount points
138
-		$mountpointExists = function ($path) use ($mountpoints) {
138
+		$mountpointExists = function($path) use ($mountpoints) {
139 139
 			foreach ($mountpoints as $mountpoint) {
140 140
 				if ($mountpoint->getShare()->getTarget() === $path) {
141 141
 					return true;
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
 
147 147
 		$i = 2;
148 148
 		while ($view->file_exists($path) || $mountpointExists($path)) {
149
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
149
+			$path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext);
150 150
 			$i++;
151 151
 		}
152 152
 
@@ -167,7 +167,7 @@  discard block
 block discarded – undo
167 167
 		// it is not a file relative to data/user/files
168 168
 		if (count($split) < 3 || $split[1] !== 'files') {
169 169
 			\OCP\Util::writeLog('file sharing',
170
-				'Can not strip userid and "files/" from path: ' . $path,
170
+				'Can not strip userid and "files/" from path: '.$path,
171 171
 				\OCP\Util::ERROR);
172 172
 			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
173 173
 		}
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
 		$sliced = array_slice($split, 2);
177 177
 		$relPath = implode('/', $sliced);
178 178
 
179
-		return '/' . $relPath;
179
+		return '/'.$relPath;
180 180
 	}
181 181
 
182 182
 	/**
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
 			$this->storage->setMountPoint($relTargetPath);
199 199
 		} catch (\Exception $e) {
200 200
 			\OCP\Util::writeLog('file sharing',
201
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
201
+				'Could not rename mount point for shared folder "'.$this->getMountPoint().'" to "'.$target.'"',
202 202
 				\OCP\Util::ERROR);
203 203
 		}
204 204
 
@@ -253,7 +253,7 @@  discard block
 block discarded – undo
253 253
 			$row = $result->fetch();
254 254
 			$result->closeCursor();
255 255
 			if ($row) {
256
-				return (int)$row['storage'];
256
+				return (int) $row['storage'];
257 257
 			}
258 258
 			return -1;
259 259
 		}
Please login to merge, or discard this patch.
Indentation   +226 added lines, -226 removed lines patch added patch discarded remove patch
@@ -36,230 +36,230 @@
 block discarded – undo
36 36
  * Shared mount points can be moved by the user
37 37
  */
38 38
 class SharedMount extends MountPoint implements MoveableMount {
39
-	/**
40
-	 * @var \OCA\Files_Sharing\SharedStorage $storage
41
-	 */
42
-	protected $storage = null;
43
-
44
-	/**
45
-	 * @var \OC\Files\View
46
-	 */
47
-	private $recipientView;
48
-
49
-	/**
50
-	 * @var string
51
-	 */
52
-	private $user;
53
-
54
-	/** @var \OCP\Share\IShare */
55
-	private $superShare;
56
-
57
-	/** @var \OCP\Share\IShare[] */
58
-	private $groupedShares;
59
-
60
-	/**
61
-	 * @param string $storage
62
-	 * @param SharedMount[] $mountpoints
63
-	 * @param array|null $arguments
64
-	 * @param \OCP\Files\Storage\IStorageFactory $loader
65
-	 */
66
-	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
67
-		$this->user = $arguments['user'];
68
-		$this->recipientView = new View('/' . $this->user . '/files');
69
-
70
-		$this->superShare = $arguments['superShare'];
71
-		$this->groupedShares = $arguments['groupedShares'];
72
-
73
-		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
74
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
75
-		$arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
76
-		parent::__construct($storage, $absMountPoint, $arguments, $loader);
77
-	}
78
-
79
-	/**
80
-	 * check if the parent folder exists otherwise move the mount point up
81
-	 *
82
-	 * @param \OCP\Share\IShare $share
83
-	 * @param SharedMount[] $mountpoints
84
-	 * @return string
85
-	 */
86
-	private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints) {
87
-
88
-		$mountPoint = basename($share->getTarget());
89
-		$parent = dirname($share->getTarget());
90
-
91
-		if (!$this->recipientView->is_dir($parent)) {
92
-			$parent = Helper::getShareFolder($this->recipientView);
93
-		}
94
-
95
-		$newMountPoint = $this->generateUniqueTarget(
96
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
97
-			$this->recipientView,
98
-			$mountpoints
99
-		);
100
-
101
-		if ($newMountPoint !== $share->getTarget()) {
102
-			$this->updateFileTarget($newMountPoint, $share);
103
-		}
104
-
105
-		return $newMountPoint;
106
-	}
107
-
108
-	/**
109
-	 * update fileTarget in the database if the mount point changed
110
-	 *
111
-	 * @param string $newPath
112
-	 * @param \OCP\Share\IShare $share
113
-	 * @return bool
114
-	 */
115
-	private function updateFileTarget($newPath, &$share) {
116
-		$share->setTarget($newPath);
117
-
118
-		foreach ($this->groupedShares as $tmpShare) {
119
-			$tmpShare->setTarget($newPath);
120
-			\OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
121
-		}
122
-	}
123
-
124
-
125
-	/**
126
-	 * @param string $path
127
-	 * @param View $view
128
-	 * @param SharedMount[] $mountpoints
129
-	 * @return mixed
130
-	 */
131
-	private function generateUniqueTarget($path, $view, array $mountpoints) {
132
-		$pathinfo = pathinfo($path);
133
-		$ext = (isset($pathinfo['extension'])) ? '.' . $pathinfo['extension'] : '';
134
-		$name = $pathinfo['filename'];
135
-		$dir = $pathinfo['dirname'];
136
-
137
-		// Helper function to find existing mount points
138
-		$mountpointExists = function ($path) use ($mountpoints) {
139
-			foreach ($mountpoints as $mountpoint) {
140
-				if ($mountpoint->getShare()->getTarget() === $path) {
141
-					return true;
142
-				}
143
-			}
144
-			return false;
145
-		};
146
-
147
-		$i = 2;
148
-		while ($view->file_exists($path) || $mountpointExists($path)) {
149
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
150
-			$i++;
151
-		}
152
-
153
-		return $path;
154
-	}
155
-
156
-	/**
157
-	 * Format a path to be relative to the /user/files/ directory
158
-	 *
159
-	 * @param string $path the absolute path
160
-	 * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
161
-	 * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
162
-	 */
163
-	protected function stripUserFilesPath($path) {
164
-		$trimmed = ltrim($path, '/');
165
-		$split = explode('/', $trimmed);
166
-
167
-		// it is not a file relative to data/user/files
168
-		if (count($split) < 3 || $split[1] !== 'files') {
169
-			\OCP\Util::writeLog('file sharing',
170
-				'Can not strip userid and "files/" from path: ' . $path,
171
-				\OCP\Util::ERROR);
172
-			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
173
-		}
174
-
175
-		// skip 'user' and 'files'
176
-		$sliced = array_slice($split, 2);
177
-		$relPath = implode('/', $sliced);
178
-
179
-		return '/' . $relPath;
180
-	}
181
-
182
-	/**
183
-	 * Move the mount point to $target
184
-	 *
185
-	 * @param string $target the target mount point
186
-	 * @return bool
187
-	 */
188
-	public function moveMount($target) {
189
-
190
-		$relTargetPath = $this->stripUserFilesPath($target);
191
-		$share = $this->storage->getShare();
192
-
193
-		$result = true;
194
-
195
-		try {
196
-			$this->updateFileTarget($relTargetPath, $share);
197
-			$this->setMountPoint($target);
198
-			$this->storage->setMountPoint($relTargetPath);
199
-		} catch (\Exception $e) {
200
-			\OCP\Util::writeLog('file sharing',
201
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
202
-				\OCP\Util::ERROR);
203
-		}
204
-
205
-		return $result;
206
-	}
207
-
208
-	/**
209
-	 * Remove the mount points
210
-	 *
211
-	 * @return bool
212
-	 */
213
-	public function removeMount() {
214
-		$mountManager = \OC\Files\Filesystem::getMountManager();
215
-		/** @var $storage \OCA\Files_Sharing\SharedStorage */
216
-		$storage = $this->getStorage();
217
-		$result = $storage->unshareStorage();
218
-		$mountManager->removeMount($this->mountPoint);
219
-
220
-		return $result;
221
-	}
222
-
223
-	/**
224
-	 * @return \OCP\Share\IShare
225
-	 */
226
-	public function getShare() {
227
-		return $this->superShare;
228
-	}
229
-
230
-	/**
231
-	 * Get the file id of the root of the storage
232
-	 *
233
-	 * @return int
234
-	 */
235
-	public function getStorageRootId() {
236
-		return $this->getShare()->getNodeId();
237
-	}
238
-
239
-	/**
240
-	 * @return int
241
-	 */
242
-	public function getNumericStorageId() {
243
-		if (!is_null($this->getShare()->getNodeCacheEntry())) {
244
-			return $this->getShare()->getNodeCacheEntry()->getStorageId();
245
-		} else {
246
-			$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
247
-
248
-			$query = $builder->select('storage')
249
-				->from('filecache')
250
-				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
251
-
252
-			$result = $query->execute();
253
-			$row = $result->fetch();
254
-			$result->closeCursor();
255
-			if ($row) {
256
-				return (int)$row['storage'];
257
-			}
258
-			return -1;
259
-		}
260
-	}
261
-
262
-	public function getMountType() {
263
-		return 'shared';
264
-	}
39
+    /**
40
+     * @var \OCA\Files_Sharing\SharedStorage $storage
41
+     */
42
+    protected $storage = null;
43
+
44
+    /**
45
+     * @var \OC\Files\View
46
+     */
47
+    private $recipientView;
48
+
49
+    /**
50
+     * @var string
51
+     */
52
+    private $user;
53
+
54
+    /** @var \OCP\Share\IShare */
55
+    private $superShare;
56
+
57
+    /** @var \OCP\Share\IShare[] */
58
+    private $groupedShares;
59
+
60
+    /**
61
+     * @param string $storage
62
+     * @param SharedMount[] $mountpoints
63
+     * @param array|null $arguments
64
+     * @param \OCP\Files\Storage\IStorageFactory $loader
65
+     */
66
+    public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
67
+        $this->user = $arguments['user'];
68
+        $this->recipientView = new View('/' . $this->user . '/files');
69
+
70
+        $this->superShare = $arguments['superShare'];
71
+        $this->groupedShares = $arguments['groupedShares'];
72
+
73
+        $newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
74
+        $absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
75
+        $arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
76
+        parent::__construct($storage, $absMountPoint, $arguments, $loader);
77
+    }
78
+
79
+    /**
80
+     * check if the parent folder exists otherwise move the mount point up
81
+     *
82
+     * @param \OCP\Share\IShare $share
83
+     * @param SharedMount[] $mountpoints
84
+     * @return string
85
+     */
86
+    private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints) {
87
+
88
+        $mountPoint = basename($share->getTarget());
89
+        $parent = dirname($share->getTarget());
90
+
91
+        if (!$this->recipientView->is_dir($parent)) {
92
+            $parent = Helper::getShareFolder($this->recipientView);
93
+        }
94
+
95
+        $newMountPoint = $this->generateUniqueTarget(
96
+            \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
97
+            $this->recipientView,
98
+            $mountpoints
99
+        );
100
+
101
+        if ($newMountPoint !== $share->getTarget()) {
102
+            $this->updateFileTarget($newMountPoint, $share);
103
+        }
104
+
105
+        return $newMountPoint;
106
+    }
107
+
108
+    /**
109
+     * update fileTarget in the database if the mount point changed
110
+     *
111
+     * @param string $newPath
112
+     * @param \OCP\Share\IShare $share
113
+     * @return bool
114
+     */
115
+    private function updateFileTarget($newPath, &$share) {
116
+        $share->setTarget($newPath);
117
+
118
+        foreach ($this->groupedShares as $tmpShare) {
119
+            $tmpShare->setTarget($newPath);
120
+            \OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
121
+        }
122
+    }
123
+
124
+
125
+    /**
126
+     * @param string $path
127
+     * @param View $view
128
+     * @param SharedMount[] $mountpoints
129
+     * @return mixed
130
+     */
131
+    private function generateUniqueTarget($path, $view, array $mountpoints) {
132
+        $pathinfo = pathinfo($path);
133
+        $ext = (isset($pathinfo['extension'])) ? '.' . $pathinfo['extension'] : '';
134
+        $name = $pathinfo['filename'];
135
+        $dir = $pathinfo['dirname'];
136
+
137
+        // Helper function to find existing mount points
138
+        $mountpointExists = function ($path) use ($mountpoints) {
139
+            foreach ($mountpoints as $mountpoint) {
140
+                if ($mountpoint->getShare()->getTarget() === $path) {
141
+                    return true;
142
+                }
143
+            }
144
+            return false;
145
+        };
146
+
147
+        $i = 2;
148
+        while ($view->file_exists($path) || $mountpointExists($path)) {
149
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
150
+            $i++;
151
+        }
152
+
153
+        return $path;
154
+    }
155
+
156
+    /**
157
+     * Format a path to be relative to the /user/files/ directory
158
+     *
159
+     * @param string $path the absolute path
160
+     * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
161
+     * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
162
+     */
163
+    protected function stripUserFilesPath($path) {
164
+        $trimmed = ltrim($path, '/');
165
+        $split = explode('/', $trimmed);
166
+
167
+        // it is not a file relative to data/user/files
168
+        if (count($split) < 3 || $split[1] !== 'files') {
169
+            \OCP\Util::writeLog('file sharing',
170
+                'Can not strip userid and "files/" from path: ' . $path,
171
+                \OCP\Util::ERROR);
172
+            throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
173
+        }
174
+
175
+        // skip 'user' and 'files'
176
+        $sliced = array_slice($split, 2);
177
+        $relPath = implode('/', $sliced);
178
+
179
+        return '/' . $relPath;
180
+    }
181
+
182
+    /**
183
+     * Move the mount point to $target
184
+     *
185
+     * @param string $target the target mount point
186
+     * @return bool
187
+     */
188
+    public function moveMount($target) {
189
+
190
+        $relTargetPath = $this->stripUserFilesPath($target);
191
+        $share = $this->storage->getShare();
192
+
193
+        $result = true;
194
+
195
+        try {
196
+            $this->updateFileTarget($relTargetPath, $share);
197
+            $this->setMountPoint($target);
198
+            $this->storage->setMountPoint($relTargetPath);
199
+        } catch (\Exception $e) {
200
+            \OCP\Util::writeLog('file sharing',
201
+                'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
202
+                \OCP\Util::ERROR);
203
+        }
204
+
205
+        return $result;
206
+    }
207
+
208
+    /**
209
+     * Remove the mount points
210
+     *
211
+     * @return bool
212
+     */
213
+    public function removeMount() {
214
+        $mountManager = \OC\Files\Filesystem::getMountManager();
215
+        /** @var $storage \OCA\Files_Sharing\SharedStorage */
216
+        $storage = $this->getStorage();
217
+        $result = $storage->unshareStorage();
218
+        $mountManager->removeMount($this->mountPoint);
219
+
220
+        return $result;
221
+    }
222
+
223
+    /**
224
+     * @return \OCP\Share\IShare
225
+     */
226
+    public function getShare() {
227
+        return $this->superShare;
228
+    }
229
+
230
+    /**
231
+     * Get the file id of the root of the storage
232
+     *
233
+     * @return int
234
+     */
235
+    public function getStorageRootId() {
236
+        return $this->getShare()->getNodeId();
237
+    }
238
+
239
+    /**
240
+     * @return int
241
+     */
242
+    public function getNumericStorageId() {
243
+        if (!is_null($this->getShare()->getNodeCacheEntry())) {
244
+            return $this->getShare()->getNodeCacheEntry()->getStorageId();
245
+        } else {
246
+            $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
247
+
248
+            $query = $builder->select('storage')
249
+                ->from('filecache')
250
+                ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
251
+
252
+            $result = $query->execute();
253
+            $row = $result->fetch();
254
+            $result->closeCursor();
255
+            if ($row) {
256
+                return (int)$row['storage'];
257
+            }
258
+            return -1;
259
+        }
260
+    }
261
+
262
+    public function getMountType() {
263
+        return 'shared';
264
+    }
265 265
 }
Please login to merge, or discard this patch.
lib/private/Cache/File.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -95,7 +95,7 @@
 block discarded – undo
95 95
 	 * @param string $key
96 96
 	 * @param mixed $value
97 97
 	 * @param int $ttl
98
-	 * @return bool|mixed
98
+	 * @return boolean
99 99
 	 * @throws \OC\ForbiddenException
100 100
 	 */
101 101
 	public function set($key, $value, $ttl = 0) {
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -51,10 +51,10 @@  discard block
 block discarded – undo
51 51
 			$rootView = new View();
52 52
 			$user = \OC::$server->getUserSession()->getUser();
53 53
 			Filesystem::initMountPoints($user->getUID());
54
-			if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
55
-				$rootView->mkdir('/' . $user->getUID() . '/cache');
54
+			if (!$rootView->file_exists('/'.$user->getUID().'/cache')) {
55
+				$rootView->mkdir('/'.$user->getUID().'/cache');
56 56
 			}
57
-			$this->storage = new View('/' . $user->getUID() . '/cache');
57
+			$this->storage = new View('/'.$user->getUID().'/cache');
58 58
 			return $this->storage;
59 59
 		} else {
60 60
 			\OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', \OCP\Util::ERROR);
@@ -104,12 +104,12 @@  discard block
 block discarded – undo
104 104
 		// unique id to avoid chunk collision, just in case
105 105
 		$uniqueId = \OC::$server->getSecureRandom()->generate(
106 106
 			16,
107
-			ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
107
+			ISecureRandom::CHAR_DIGITS.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER
108 108
 		);
109 109
 
110 110
 		// use part file to prevent hasKey() to find the key
111 111
 		// while it is being written
112
-		$keyPart = $key . '.' . $uniqueId . '.part';
112
+		$keyPart = $key.'.'.$uniqueId.'.part';
113 113
 		if ($storage and $storage->file_put_contents($keyPart, $value)) {
114 114
 			if ($ttl === 0) {
115 115
 				$ttl = 86400; // 60*60*24
@@ -158,7 +158,7 @@  discard block
 block discarded – undo
158 158
 			if (is_resource($dh)) {
159 159
 				while (($file = readdir($dh)) !== false) {
160 160
 					if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
161
-						$storage->unlink('/' . $file);
161
+						$storage->unlink('/'.$file);
162 162
 					}
163 163
 				}
164 164
 			}
@@ -183,17 +183,17 @@  discard block
 block discarded – undo
183 183
 			while (($file = readdir($dh)) !== false) {
184 184
 				if ($file != '.' and $file != '..') {
185 185
 					try {
186
-						$mtime = $storage->filemtime('/' . $file);
186
+						$mtime = $storage->filemtime('/'.$file);
187 187
 						if ($mtime < $now) {
188
-							$storage->unlink('/' . $file);
188
+							$storage->unlink('/'.$file);
189 189
 						}
190 190
 					} catch (\OCP\Lock\LockedException $e) {
191 191
 						// ignore locked chunks
192
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
192
+						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "'.$file.'"', array('app' => 'core'));
193 193
 					} catch (\OCP\Files\ForbiddenException $e) {
194
-						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', array('app' => 'core'));
194
+						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "'.$file.'"', array('app' => 'core'));
195 195
 					} catch (\OCP\Files\LockNotAcquiredException $e) {
196
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
196
+						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "'.$file.'"', array('app' => 'core'));
197 197
 					}
198 198
 				}
199 199
 			}
Please login to merge, or discard this patch.
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -33,170 +33,170 @@
 block discarded – undo
33 33
 
34 34
 class File implements ICache {
35 35
 
36
-	/** @var View */
37
-	protected $storage;
36
+    /** @var View */
37
+    protected $storage;
38 38
 
39
-	/**
40
-	 * Returns the cache storage for the logged in user
41
-	 *
42
-	 * @return \OC\Files\View cache storage
43
-	 * @throws \OC\ForbiddenException
44
-	 * @throws \OC\User\NoUserException
45
-	 */
46
-	protected function getStorage() {
47
-		if (isset($this->storage)) {
48
-			return $this->storage;
49
-		}
50
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
51
-			$rootView = new View();
52
-			$user = \OC::$server->getUserSession()->getUser();
53
-			Filesystem::initMountPoints($user->getUID());
54
-			if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
55
-				$rootView->mkdir('/' . $user->getUID() . '/cache');
56
-			}
57
-			$this->storage = new View('/' . $user->getUID() . '/cache');
58
-			return $this->storage;
59
-		} else {
60
-			\OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', \OCP\Util::ERROR);
61
-			throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
62
-		}
63
-	}
39
+    /**
40
+     * Returns the cache storage for the logged in user
41
+     *
42
+     * @return \OC\Files\View cache storage
43
+     * @throws \OC\ForbiddenException
44
+     * @throws \OC\User\NoUserException
45
+     */
46
+    protected function getStorage() {
47
+        if (isset($this->storage)) {
48
+            return $this->storage;
49
+        }
50
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
51
+            $rootView = new View();
52
+            $user = \OC::$server->getUserSession()->getUser();
53
+            Filesystem::initMountPoints($user->getUID());
54
+            if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
55
+                $rootView->mkdir('/' . $user->getUID() . '/cache');
56
+            }
57
+            $this->storage = new View('/' . $user->getUID() . '/cache');
58
+            return $this->storage;
59
+        } else {
60
+            \OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', \OCP\Util::ERROR);
61
+            throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
62
+        }
63
+    }
64 64
 
65
-	/**
66
-	 * @param string $key
67
-	 * @return mixed|null
68
-	 * @throws \OC\ForbiddenException
69
-	 */
70
-	public function get($key) {
71
-		$result = null;
72
-		if ($this->hasKey($key)) {
73
-			$storage = $this->getStorage();
74
-			$result = $storage->file_get_contents($key);
75
-		}
76
-		return $result;
77
-	}
65
+    /**
66
+     * @param string $key
67
+     * @return mixed|null
68
+     * @throws \OC\ForbiddenException
69
+     */
70
+    public function get($key) {
71
+        $result = null;
72
+        if ($this->hasKey($key)) {
73
+            $storage = $this->getStorage();
74
+            $result = $storage->file_get_contents($key);
75
+        }
76
+        return $result;
77
+    }
78 78
 
79
-	/**
80
-	 * Returns the size of the stored/cached data
81
-	 *
82
-	 * @param string $key
83
-	 * @return int
84
-	 */
85
-	public function size($key) {
86
-		$result = 0;
87
-		if ($this->hasKey($key)) {
88
-			$storage = $this->getStorage();
89
-			$result = $storage->filesize($key);
90
-		}
91
-		return $result;
92
-	}
79
+    /**
80
+     * Returns the size of the stored/cached data
81
+     *
82
+     * @param string $key
83
+     * @return int
84
+     */
85
+    public function size($key) {
86
+        $result = 0;
87
+        if ($this->hasKey($key)) {
88
+            $storage = $this->getStorage();
89
+            $result = $storage->filesize($key);
90
+        }
91
+        return $result;
92
+    }
93 93
 
94
-	/**
95
-	 * @param string $key
96
-	 * @param mixed $value
97
-	 * @param int $ttl
98
-	 * @return bool|mixed
99
-	 * @throws \OC\ForbiddenException
100
-	 */
101
-	public function set($key, $value, $ttl = 0) {
102
-		$storage = $this->getStorage();
103
-		$result = false;
104
-		// unique id to avoid chunk collision, just in case
105
-		$uniqueId = \OC::$server->getSecureRandom()->generate(
106
-			16,
107
-			ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
108
-		);
94
+    /**
95
+     * @param string $key
96
+     * @param mixed $value
97
+     * @param int $ttl
98
+     * @return bool|mixed
99
+     * @throws \OC\ForbiddenException
100
+     */
101
+    public function set($key, $value, $ttl = 0) {
102
+        $storage = $this->getStorage();
103
+        $result = false;
104
+        // unique id to avoid chunk collision, just in case
105
+        $uniqueId = \OC::$server->getSecureRandom()->generate(
106
+            16,
107
+            ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
108
+        );
109 109
 
110
-		// use part file to prevent hasKey() to find the key
111
-		// while it is being written
112
-		$keyPart = $key . '.' . $uniqueId . '.part';
113
-		if ($storage and $storage->file_put_contents($keyPart, $value)) {
114
-			if ($ttl === 0) {
115
-				$ttl = 86400; // 60*60*24
116
-			}
117
-			$result = $storage->touch($keyPart, time() + $ttl);
118
-			$result &= $storage->rename($keyPart, $key);
119
-		}
120
-		return $result;
121
-	}
110
+        // use part file to prevent hasKey() to find the key
111
+        // while it is being written
112
+        $keyPart = $key . '.' . $uniqueId . '.part';
113
+        if ($storage and $storage->file_put_contents($keyPart, $value)) {
114
+            if ($ttl === 0) {
115
+                $ttl = 86400; // 60*60*24
116
+            }
117
+            $result = $storage->touch($keyPart, time() + $ttl);
118
+            $result &= $storage->rename($keyPart, $key);
119
+        }
120
+        return $result;
121
+    }
122 122
 
123
-	/**
124
-	 * @param string $key
125
-	 * @return bool
126
-	 * @throws \OC\ForbiddenException
127
-	 */
128
-	public function hasKey($key) {
129
-		$storage = $this->getStorage();
130
-		if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
131
-			return true;
132
-		}
133
-		return false;
134
-	}
123
+    /**
124
+     * @param string $key
125
+     * @return bool
126
+     * @throws \OC\ForbiddenException
127
+     */
128
+    public function hasKey($key) {
129
+        $storage = $this->getStorage();
130
+        if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
131
+            return true;
132
+        }
133
+        return false;
134
+    }
135 135
 
136
-	/**
137
-	 * @param string $key
138
-	 * @return bool|mixed
139
-	 * @throws \OC\ForbiddenException
140
-	 */
141
-	public function remove($key) {
142
-		$storage = $this->getStorage();
143
-		if (!$storage) {
144
-			return false;
145
-		}
146
-		return $storage->unlink($key);
147
-	}
136
+    /**
137
+     * @param string $key
138
+     * @return bool|mixed
139
+     * @throws \OC\ForbiddenException
140
+     */
141
+    public function remove($key) {
142
+        $storage = $this->getStorage();
143
+        if (!$storage) {
144
+            return false;
145
+        }
146
+        return $storage->unlink($key);
147
+    }
148 148
 
149
-	/**
150
-	 * @param string $prefix
151
-	 * @return bool
152
-	 * @throws \OC\ForbiddenException
153
-	 */
154
-	public function clear($prefix = '') {
155
-		$storage = $this->getStorage();
156
-		if ($storage and $storage->is_dir('/')) {
157
-			$dh = $storage->opendir('/');
158
-			if (is_resource($dh)) {
159
-				while (($file = readdir($dh)) !== false) {
160
-					if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
161
-						$storage->unlink('/' . $file);
162
-					}
163
-				}
164
-			}
165
-		}
166
-		return true;
167
-	}
149
+    /**
150
+     * @param string $prefix
151
+     * @return bool
152
+     * @throws \OC\ForbiddenException
153
+     */
154
+    public function clear($prefix = '') {
155
+        $storage = $this->getStorage();
156
+        if ($storage and $storage->is_dir('/')) {
157
+            $dh = $storage->opendir('/');
158
+            if (is_resource($dh)) {
159
+                while (($file = readdir($dh)) !== false) {
160
+                    if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
161
+                        $storage->unlink('/' . $file);
162
+                    }
163
+                }
164
+            }
165
+        }
166
+        return true;
167
+    }
168 168
 
169
-	/**
170
-	 * Runs GC
171
-	 * @throws \OC\ForbiddenException
172
-	 */
173
-	public function gc() {
174
-		$storage = $this->getStorage();
175
-		if ($storage and $storage->is_dir('/')) {
176
-			// extra hour safety, in case of stray part chunks that take longer to write,
177
-			// because touch() is only called after the chunk was finished
178
-			$now = time() - 3600;
179
-			$dh = $storage->opendir('/');
180
-			if (!is_resource($dh)) {
181
-				return null;
182
-			}
183
-			while (($file = readdir($dh)) !== false) {
184
-				if ($file != '.' and $file != '..') {
185
-					try {
186
-						$mtime = $storage->filemtime('/' . $file);
187
-						if ($mtime < $now) {
188
-							$storage->unlink('/' . $file);
189
-						}
190
-					} catch (\OCP\Lock\LockedException $e) {
191
-						// ignore locked chunks
192
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
193
-					} catch (\OCP\Files\ForbiddenException $e) {
194
-						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', array('app' => 'core'));
195
-					} catch (\OCP\Files\LockNotAcquiredException $e) {
196
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
197
-					}
198
-				}
199
-			}
200
-		}
201
-	}
169
+    /**
170
+     * Runs GC
171
+     * @throws \OC\ForbiddenException
172
+     */
173
+    public function gc() {
174
+        $storage = $this->getStorage();
175
+        if ($storage and $storage->is_dir('/')) {
176
+            // extra hour safety, in case of stray part chunks that take longer to write,
177
+            // because touch() is only called after the chunk was finished
178
+            $now = time() - 3600;
179
+            $dh = $storage->opendir('/');
180
+            if (!is_resource($dh)) {
181
+                return null;
182
+            }
183
+            while (($file = readdir($dh)) !== false) {
184
+                if ($file != '.' and $file != '..') {
185
+                    try {
186
+                        $mtime = $storage->filemtime('/' . $file);
187
+                        if ($mtime < $now) {
188
+                            $storage->unlink('/' . $file);
189
+                        }
190
+                    } catch (\OCP\Lock\LockedException $e) {
191
+                        // ignore locked chunks
192
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
193
+                    } catch (\OCP\Files\ForbiddenException $e) {
194
+                        \OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', array('app' => 'core'));
195
+                    } catch (\OCP\Files\LockNotAcquiredException $e) {
196
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', array('app' => 'core'));
197
+                    }
198
+                }
199
+            }
200
+        }
201
+    }
202 202
 }
Please login to merge, or discard this patch.