Completed
Pull Request — master (#8603)
by Björn
18:17 queued 02:41
created
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.
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",
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
 		}
699 699
 		$postFix = (substr($path, -1) === '/') ? '/' : '';
700 700
 		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
701
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
701
+		$mount = Filesystem::getMountManager()->find($absolutePath.$postFix);
702 702
 		if ($mount and $mount->getInternalPath($absolutePath) === '') {
703 703
 			return $this->removeMount($mount, $absolutePath);
704 704
 		}
@@ -818,7 +818,7 @@  discard block
 block discarded – undo
818 818
 								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
819 819
 							}
820 820
 						}
821
-					} catch(\Exception $e) {
821
+					} catch (\Exception $e) {
822 822
 						throw $e;
823 823
 					} finally {
824 824
 						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
@@ -842,7 +842,7 @@  discard block
 block discarded – undo
842 842
 						}
843 843
 					}
844 844
 				}
845
-			} catch(\Exception $e) {
845
+			} catch (\Exception $e) {
846 846
 				throw $e;
847 847
 			} finally {
848 848
 				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
@@ -975,7 +975,7 @@  discard block
 block discarded – undo
975 975
 				$hooks[] = 'write';
976 976
 				break;
977 977
 			default:
978
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
978
+				\OCP\Util::writeLog('core', 'invalid mode ('.$mode.') for '.$path, \OCP\Util::ERROR);
979 979
 		}
980 980
 
981 981
 		if ($mode !== 'r' && $mode !== 'w') {
@@ -1079,7 +1079,7 @@  discard block
 block discarded – undo
1079 1079
 					array(Filesystem::signal_param_path => $this->getHookPath($path))
1080 1080
 				);
1081 1081
 			}
1082
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1082
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1083 1083
 			if ($storage) {
1084 1084
 				return $storage->hash($type, $internalPath, $raw);
1085 1085
 			}
@@ -1133,7 +1133,7 @@  discard block
 block discarded – undo
1133 1133
 
1134 1134
 			$run = $this->runHooks($hooks, $path);
1135 1135
 			/** @var \OC\Files\Storage\Storage $storage */
1136
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1136
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1137 1137
 			if ($run and $storage) {
1138 1138
 				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1139 1139
 					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
@@ -1172,7 +1172,7 @@  discard block
 block discarded – undo
1172 1172
 					$unlockLater = true;
1173 1173
 					// make sure our unlocking callback will still be called if connection is aborted
1174 1174
 					ignore_user_abort(true);
1175
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1175
+					$result = CallbackWrapper::wrap($result, null, null, function() use ($hooks, $path) {
1176 1176
 						if (in_array('write', $hooks)) {
1177 1177
 							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1178 1178
 						} else if (in_array('read', $hooks)) {
@@ -1233,7 +1233,7 @@  discard block
 block discarded – undo
1233 1233
 			return true;
1234 1234
 		}
1235 1235
 
1236
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1236
+		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot.'/');
1237 1237
 	}
1238 1238
 
1239 1239
 	/**
@@ -1252,7 +1252,7 @@  discard block
 block discarded – undo
1252 1252
 				if ($hook != 'read') {
1253 1253
 					\OC_Hook::emit(
1254 1254
 						Filesystem::CLASSNAME,
1255
-						$prefix . $hook,
1255
+						$prefix.$hook,
1256 1256
 						array(
1257 1257
 							Filesystem::signal_param_run => &$run,
1258 1258
 							Filesystem::signal_param_path => $path
@@ -1261,7 +1261,7 @@  discard block
 block discarded – undo
1261 1261
 				} elseif (!$post) {
1262 1262
 					\OC_Hook::emit(
1263 1263
 						Filesystem::CLASSNAME,
1264
-						$prefix . $hook,
1264
+						$prefix.$hook,
1265 1265
 						array(
1266 1266
 							Filesystem::signal_param_path => $path
1267 1267
 						)
@@ -1356,7 +1356,7 @@  discard block
 block discarded – undo
1356 1356
 			return $this->getPartFileInfo($path);
1357 1357
 		}
1358 1358
 		$relativePath = $path;
1359
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1359
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1360 1360
 
1361 1361
 		$mount = Filesystem::getMountManager()->find($path);
1362 1362
 		if (!$mount) {
@@ -1383,7 +1383,7 @@  discard block
 block discarded – undo
1383 1383
 					//add the sizes of other mount points to the folder
1384 1384
 					$extOnly = ($includeMountPoints === 'ext');
1385 1385
 					$mounts = Filesystem::getMountManager()->findIn($path);
1386
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1386
+					$info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) {
1387 1387
 						$subStorage = $mount->getStorage();
1388 1388
 						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1389 1389
 					}));
@@ -1433,12 +1433,12 @@  discard block
 block discarded – undo
1433 1433
 			/**
1434 1434
 			 * @var \OC\Files\FileInfo[] $files
1435 1435
 			 */
1436
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1436
+			$files = array_map(function(ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1437 1437
 				if ($sharingDisabled) {
1438 1438
 					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1439 1439
 				}
1440 1440
 				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1441
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1441
+				return new FileInfo($path.'/'.$content['name'], $storage, $content['path'], $content, $mount, $owner);
1442 1442
 			}, $contents);
1443 1443
 
1444 1444
 			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
@@ -1462,7 +1462,7 @@  discard block
 block discarded – undo
1462 1462
 						} catch (\Exception $e) {
1463 1463
 							// sometimes when the storage is not available it can be any exception
1464 1464
 							\OC::$server->getLogger()->logException($e, [
1465
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1465
+								'message' => 'Exception while scanning storage "'.$subStorage->getId().'"',
1466 1466
 								'level' => \OCP\Util::ERROR,
1467 1467
 								'app' => 'lib',
1468 1468
 							]);
@@ -1500,7 +1500,7 @@  discard block
 block discarded – undo
1500 1500
 									break;
1501 1501
 								}
1502 1502
 							}
1503
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1503
+							$rootEntry['path'] = substr(Filesystem::normalizePath($path.'/'.$rootEntry['name']), strlen($user) + 2); // full path without /$user/
1504 1504
 
1505 1505
 							// if sharing was disabled for the user we remove the share permissions
1506 1506
 							if (\OCP\Util::isSharingDisabledForUser()) {
@@ -1508,14 +1508,14 @@  discard block
 block discarded – undo
1508 1508
 							}
1509 1509
 
1510 1510
 							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1511
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1511
+							$files[] = new FileInfo($path.'/'.$rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1512 1512
 						}
1513 1513
 					}
1514 1514
 				}
1515 1515
 			}
1516 1516
 
1517 1517
 			if ($mimetype_filter) {
1518
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1518
+				$files = array_filter($files, function(FileInfo $file) use ($mimetype_filter) {
1519 1519
 					if (strpos($mimetype_filter, '/')) {
1520 1520
 						return $file->getMimetype() === $mimetype_filter;
1521 1521
 					} else {
@@ -1544,7 +1544,7 @@  discard block
 block discarded – undo
1544 1544
 		if ($data instanceof FileInfo) {
1545 1545
 			$data = $data->getData();
1546 1546
 		}
1547
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1547
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1548 1548
 		/**
1549 1549
 		 * @var \OC\Files\Storage\Storage $storage
1550 1550
 		 * @var string $internalPath
@@ -1571,7 +1571,7 @@  discard block
 block discarded – undo
1571 1571
 	 * @return FileInfo[]
1572 1572
 	 */
1573 1573
 	public function search($query) {
1574
-		return $this->searchCommon('search', array('%' . $query . '%'));
1574
+		return $this->searchCommon('search', array('%'.$query.'%'));
1575 1575
 	}
1576 1576
 
1577 1577
 	/**
@@ -1622,10 +1622,10 @@  discard block
 block discarded – undo
1622 1622
 
1623 1623
 			$results = call_user_func_array(array($cache, $method), $args);
1624 1624
 			foreach ($results as $result) {
1625
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1625
+				if (substr($mountPoint.$result['path'], 0, $rootLength + 1) === $this->fakeRoot.'/') {
1626 1626
 					$internalPath = $result['path'];
1627
-					$path = $mountPoint . $result['path'];
1628
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1627
+					$path = $mountPoint.$result['path'];
1628
+					$result['path'] = substr($mountPoint.$result['path'], $rootLength);
1629 1629
 					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1630 1630
 					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1631 1631
 				}
@@ -1643,8 +1643,8 @@  discard block
 block discarded – undo
1643 1643
 					if ($results) {
1644 1644
 						foreach ($results as $result) {
1645 1645
 							$internalPath = $result['path'];
1646
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1647
-							$path = rtrim($mountPoint . $internalPath, '/');
1646
+							$result['path'] = rtrim($relativeMountPoint.$result['path'], '/');
1647
+							$path = rtrim($mountPoint.$internalPath, '/');
1648 1648
 							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1649 1649
 							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1650 1650
 						}
@@ -1665,7 +1665,7 @@  discard block
 block discarded – undo
1665 1665
 	public function getOwner($path) {
1666 1666
 		$info = $this->getFileInfo($path);
1667 1667
 		if (!$info) {
1668
-			throw new NotFoundException($path . ' not found while trying to get owner');
1668
+			throw new NotFoundException($path.' not found while trying to get owner');
1669 1669
 		}
1670 1670
 		return $info->getOwner()->getUID();
1671 1671
 	}
@@ -1699,7 +1699,7 @@  discard block
 block discarded – undo
1699 1699
 	 * @return string
1700 1700
 	 */
1701 1701
 	public function getPath($id) {
1702
-		$id = (int)$id;
1702
+		$id = (int) $id;
1703 1703
 		$manager = Filesystem::getMountManager();
1704 1704
 		$mounts = $manager->findIn($this->fakeRoot);
1705 1705
 		$mounts[] = $manager->find($this->fakeRoot);
@@ -1714,7 +1714,7 @@  discard block
 block discarded – undo
1714 1714
 				$cache = $mount->getStorage()->getCache();
1715 1715
 				$internalPath = $cache->getPathById($id);
1716 1716
 				if (is_string($internalPath)) {
1717
-					$fullPath = $mount->getMountPoint() . $internalPath;
1717
+					$fullPath = $mount->getMountPoint().$internalPath;
1718 1718
 					if (!is_null($path = $this->getRelativePath($fullPath))) {
1719 1719
 						return $path;
1720 1720
 					}
@@ -1757,10 +1757,10 @@  discard block
 block discarded – undo
1757 1757
 		}
1758 1758
 
1759 1759
 		// note: cannot use the view because the target is already locked
1760
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1760
+		$fileId = (int) $targetStorage->getCache()->getId($targetInternalPath);
1761 1761
 		if ($fileId === -1) {
1762 1762
 			// target might not exist, need to check parent instead
1763
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1763
+			$fileId = (int) $targetStorage->getCache()->getId(dirname($targetInternalPath));
1764 1764
 		}
1765 1765
 
1766 1766
 		// check if any of the parents were shared by the current owner (include collections)
@@ -1860,7 +1860,7 @@  discard block
 block discarded – undo
1860 1860
 		$resultPath = '';
1861 1861
 		foreach ($parts as $part) {
1862 1862
 			if ($part) {
1863
-				$resultPath .= '/' . $part;
1863
+				$resultPath .= '/'.$part;
1864 1864
 				$result[] = $resultPath;
1865 1865
 			}
1866 1866
 		}
@@ -2123,16 +2123,16 @@  discard block
 block discarded – undo
2123 2123
 	public function getUidAndFilename($filename) {
2124 2124
 		$info = $this->getFileInfo($filename);
2125 2125
 		if (!$info instanceof \OCP\Files\FileInfo) {
2126
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2126
+			throw new NotFoundException($this->getAbsolutePath($filename).' not found');
2127 2127
 		}
2128 2128
 		$uid = $info->getOwner()->getUID();
2129 2129
 		if ($uid != \OCP\User::getUser()) {
2130 2130
 			Filesystem::initMountPoints($uid);
2131
-			$ownerView = new View('/' . $uid . '/files');
2131
+			$ownerView = new View('/'.$uid.'/files');
2132 2132
 			try {
2133 2133
 				$filename = $ownerView->getPath($info['fileid']);
2134 2134
 			} catch (NotFoundException $e) {
2135
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2135
+				throw new NotFoundException('File with id '.$info['fileid'].' not found for user '.$uid);
2136 2136
 			}
2137 2137
 		}
2138 2138
 		return [$uid, $filename];
@@ -2149,7 +2149,7 @@  discard block
 block discarded – undo
2149 2149
 		$directoryParts = array_filter($directoryParts);
2150 2150
 		foreach ($directoryParts as $key => $part) {
2151 2151
 			$currentPathElements = array_slice($directoryParts, 0, $key);
2152
-			$currentPath = '/' . implode('/', $currentPathElements);
2152
+			$currentPath = '/'.implode('/', $currentPathElements);
2153 2153
 			if ($this->is_file($currentPath)) {
2154 2154
 				return false;
2155 2155
 			}
Please login to merge, or discard this patch.
Indentation   +2080 added lines, -2080 removed lines patch added patch discarded remove patch
@@ -80,2084 +80,2084 @@
 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
-			return $this->filesize($path);
430
-		}
431
-		return false;
432
-	}
433
-
434
-	/**
435
-	 * @param string $path
436
-	 * @param int $from
437
-	 * @param int $to
438
-	 * @return bool|mixed
439
-	 * @throws \OCP\Files\InvalidPathException
440
-	 * @throws \OCP\Files\UnseekableException
441
-	 */
442
-	public function readfilePart($path, $from, $to) {
443
-		$this->assertPathLength($path);
444
-		@ob_end_clean();
445
-		$handle = $this->fopen($path, 'rb');
446
-		if ($handle) {
447
-			$chunkSize = 8192; // 8 kB chunks
448
-			$startReading = true;
449
-
450
-			if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
451
-				// forward file handle via chunked fread because fseek seem to have failed
452
-
453
-				$end = $from + 1;
454
-				while (!feof($handle) && ftell($handle) < $end) {
455
-					$len = $from - ftell($handle);
456
-					if ($len > $chunkSize) {
457
-						$len = $chunkSize;
458
-					}
459
-					$result = fread($handle, $len);
460
-
461
-					if ($result === false) {
462
-						$startReading = false;
463
-						break;
464
-					}
465
-				}
466
-			}
467
-
468
-			if ($startReading) {
469
-				$end = $to + 1;
470
-				while (!feof($handle) && ftell($handle) < $end) {
471
-					$len = $end - ftell($handle);
472
-					if ($len > $chunkSize) {
473
-						$len = $chunkSize;
474
-					}
475
-					echo fread($handle, $len);
476
-					flush();
477
-				}
478
-				return ftell($handle) - $from;
479
-			}
480
-
481
-			throw new \OCP\Files\UnseekableException('fseek error');
482
-		}
483
-		return false;
484
-	}
485
-
486
-	/**
487
-	 * @param string $path
488
-	 * @return mixed
489
-	 */
490
-	public function isCreatable($path) {
491
-		return $this->basicOperation('isCreatable', $path);
492
-	}
493
-
494
-	/**
495
-	 * @param string $path
496
-	 * @return mixed
497
-	 */
498
-	public function isReadable($path) {
499
-		return $this->basicOperation('isReadable', $path);
500
-	}
501
-
502
-	/**
503
-	 * @param string $path
504
-	 * @return mixed
505
-	 */
506
-	public function isUpdatable($path) {
507
-		return $this->basicOperation('isUpdatable', $path);
508
-	}
509
-
510
-	/**
511
-	 * @param string $path
512
-	 * @return bool|mixed
513
-	 */
514
-	public function isDeletable($path) {
515
-		$absolutePath = $this->getAbsolutePath($path);
516
-		$mount = Filesystem::getMountManager()->find($absolutePath);
517
-		if ($mount->getInternalPath($absolutePath) === '') {
518
-			return $mount instanceof MoveableMount;
519
-		}
520
-		return $this->basicOperation('isDeletable', $path);
521
-	}
522
-
523
-	/**
524
-	 * @param string $path
525
-	 * @return mixed
526
-	 */
527
-	public function isSharable($path) {
528
-		return $this->basicOperation('isSharable', $path);
529
-	}
530
-
531
-	/**
532
-	 * @param string $path
533
-	 * @return bool|mixed
534
-	 */
535
-	public function file_exists($path) {
536
-		if ($path == '/') {
537
-			return true;
538
-		}
539
-		return $this->basicOperation('file_exists', $path);
540
-	}
541
-
542
-	/**
543
-	 * @param string $path
544
-	 * @return mixed
545
-	 */
546
-	public function filemtime($path) {
547
-		return $this->basicOperation('filemtime', $path);
548
-	}
549
-
550
-	/**
551
-	 * @param string $path
552
-	 * @param int|string $mtime
553
-	 * @return bool
554
-	 */
555
-	public function touch($path, $mtime = null) {
556
-		if (!is_null($mtime) and !is_numeric($mtime)) {
557
-			$mtime = strtotime($mtime);
558
-		}
559
-
560
-		$hooks = array('touch');
561
-
562
-		if (!$this->file_exists($path)) {
563
-			$hooks[] = 'create';
564
-			$hooks[] = 'write';
565
-		}
566
-		$result = $this->basicOperation('touch', $path, $hooks, $mtime);
567
-		if (!$result) {
568
-			// If create file fails because of permissions on external storage like SMB folders,
569
-			// check file exists and return false if not.
570
-			if (!$this->file_exists($path)) {
571
-				return false;
572
-			}
573
-			if (is_null($mtime)) {
574
-				$mtime = time();
575
-			}
576
-			//if native touch fails, we emulate it by changing the mtime in the cache
577
-			$this->putFileInfo($path, array('mtime' => floor($mtime)));
578
-		}
579
-		return true;
580
-	}
581
-
582
-	/**
583
-	 * @param string $path
584
-	 * @return mixed
585
-	 */
586
-	public function file_get_contents($path) {
587
-		return $this->basicOperation('file_get_contents', $path, array('read'));
588
-	}
589
-
590
-	/**
591
-	 * @param bool $exists
592
-	 * @param string $path
593
-	 * @param bool $run
594
-	 */
595
-	protected function emit_file_hooks_pre($exists, $path, &$run) {
596
-		if (!$exists) {
597
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
598
-				Filesystem::signal_param_path => $this->getHookPath($path),
599
-				Filesystem::signal_param_run => &$run,
600
-			));
601
-		} else {
602
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
603
-				Filesystem::signal_param_path => $this->getHookPath($path),
604
-				Filesystem::signal_param_run => &$run,
605
-			));
606
-		}
607
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
608
-			Filesystem::signal_param_path => $this->getHookPath($path),
609
-			Filesystem::signal_param_run => &$run,
610
-		));
611
-	}
612
-
613
-	/**
614
-	 * @param bool $exists
615
-	 * @param string $path
616
-	 */
617
-	protected function emit_file_hooks_post($exists, $path) {
618
-		if (!$exists) {
619
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
620
-				Filesystem::signal_param_path => $this->getHookPath($path),
621
-			));
622
-		} else {
623
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
624
-				Filesystem::signal_param_path => $this->getHookPath($path),
625
-			));
626
-		}
627
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
628
-			Filesystem::signal_param_path => $this->getHookPath($path),
629
-		));
630
-	}
631
-
632
-	/**
633
-	 * @param string $path
634
-	 * @param mixed $data
635
-	 * @return bool|mixed
636
-	 * @throws \Exception
637
-	 */
638
-	public function file_put_contents($path, $data) {
639
-		if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
640
-			$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
641
-			if (Filesystem::isValidPath($path)
642
-				and !Filesystem::isFileBlacklisted($path)
643
-			) {
644
-				$path = $this->getRelativePath($absolutePath);
645
-
646
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
647
-
648
-				$exists = $this->file_exists($path);
649
-				$run = true;
650
-				if ($this->shouldEmitHooks($path)) {
651
-					$this->emit_file_hooks_pre($exists, $path, $run);
652
-				}
653
-				if (!$run) {
654
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
655
-					return false;
656
-				}
657
-
658
-				$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
659
-
660
-				/** @var \OC\Files\Storage\Storage $storage */
661
-				list($storage, $internalPath) = $this->resolvePath($path);
662
-				$target = $storage->fopen($internalPath, 'w');
663
-				if ($target) {
664
-					list (, $result) = \OC_Helper::streamCopy($data, $target);
665
-					fclose($target);
666
-					fclose($data);
667
-
668
-					$this->writeUpdate($storage, $internalPath);
669
-
670
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
671
-
672
-					if ($this->shouldEmitHooks($path) && $result !== false) {
673
-						$this->emit_file_hooks_post($exists, $path);
674
-					}
675
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
676
-					return $result;
677
-				} else {
678
-					$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
679
-					return false;
680
-				}
681
-			} else {
682
-				return false;
683
-			}
684
-		} else {
685
-			$hooks = $this->file_exists($path) ? array('update', 'write') : array('create', 'write');
686
-			return $this->basicOperation('file_put_contents', $path, $hooks, $data);
687
-		}
688
-	}
689
-
690
-	/**
691
-	 * @param string $path
692
-	 * @return bool|mixed
693
-	 */
694
-	public function unlink($path) {
695
-		if ($path === '' || $path === '/') {
696
-			// do not allow deleting the root
697
-			return false;
698
-		}
699
-		$postFix = (substr($path, -1) === '/') ? '/' : '';
700
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
701
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
702
-		if ($mount and $mount->getInternalPath($absolutePath) === '') {
703
-			return $this->removeMount($mount, $absolutePath);
704
-		}
705
-		if ($this->is_dir($path)) {
706
-			$result = $this->basicOperation('rmdir', $path, ['delete']);
707
-		} else {
708
-			$result = $this->basicOperation('unlink', $path, ['delete']);
709
-		}
710
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
711
-			$storage = $mount->getStorage();
712
-			$internalPath = $mount->getInternalPath($absolutePath);
713
-			$storage->getUpdater()->remove($internalPath);
714
-			return true;
715
-		} else {
716
-			return $result;
717
-		}
718
-	}
719
-
720
-	/**
721
-	 * @param string $directory
722
-	 * @return bool|mixed
723
-	 */
724
-	public function deleteAll($directory) {
725
-		return $this->rmdir($directory);
726
-	}
727
-
728
-	/**
729
-	 * Rename/move a file or folder from the source path to target path.
730
-	 *
731
-	 * @param string $path1 source path
732
-	 * @param string $path2 target path
733
-	 *
734
-	 * @return bool|mixed
735
-	 */
736
-	public function rename($path1, $path2) {
737
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
738
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
739
-		$result = false;
740
-		if (
741
-			Filesystem::isValidPath($path2)
742
-			and Filesystem::isValidPath($path1)
743
-			and !Filesystem::isFileBlacklisted($path2)
744
-		) {
745
-			$path1 = $this->getRelativePath($absolutePath1);
746
-			$path2 = $this->getRelativePath($absolutePath2);
747
-			$exists = $this->file_exists($path2);
748
-
749
-			if ($path1 == null or $path2 == null) {
750
-				return false;
751
-			}
752
-
753
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
754
-			try {
755
-				$this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
756
-
757
-				$run = true;
758
-				if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
759
-					// if it was a rename from a part file to a regular file it was a write and not a rename operation
760
-					$this->emit_file_hooks_pre($exists, $path2, $run);
761
-				} elseif ($this->shouldEmitHooks($path1)) {
762
-					\OC_Hook::emit(
763
-						Filesystem::CLASSNAME, Filesystem::signal_rename,
764
-						array(
765
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
766
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
767
-							Filesystem::signal_param_run => &$run
768
-						)
769
-					);
770
-				}
771
-				if ($run) {
772
-					$this->verifyPath(dirname($path2), basename($path2));
773
-
774
-					$manager = Filesystem::getMountManager();
775
-					$mount1 = $this->getMount($path1);
776
-					$mount2 = $this->getMount($path2);
777
-					$storage1 = $mount1->getStorage();
778
-					$storage2 = $mount2->getStorage();
779
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
780
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
781
-
782
-					$this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
783
-					try {
784
-						$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
785
-
786
-						if ($internalPath1 === '') {
787
-							if ($mount1 instanceof MoveableMount) {
788
-								if ($this->isTargetAllowed($absolutePath2)) {
789
-									/**
790
-									 * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
791
-									 */
792
-									$sourceMountPoint = $mount1->getMountPoint();
793
-									$result = $mount1->moveMount($absolutePath2);
794
-									$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
795
-								} else {
796
-									$result = false;
797
-								}
798
-							} else {
799
-								$result = false;
800
-							}
801
-							// moving a file/folder within the same mount point
802
-						} elseif ($storage1 === $storage2) {
803
-							if ($storage1) {
804
-								$result = $storage1->rename($internalPath1, $internalPath2);
805
-							} else {
806
-								$result = false;
807
-							}
808
-							// moving a file/folder between storages (from $storage1 to $storage2)
809
-						} else {
810
-							$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
811
-						}
812
-
813
-						if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
814
-							// if it was a rename from a part file to a regular file it was a write and not a rename operation
815
-							$this->writeUpdate($storage2, $internalPath2);
816
-						} else if ($result) {
817
-							if ($internalPath1 !== '') { // don't do a cache update for moved mounts
818
-								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
819
-							}
820
-						}
821
-					} catch(\Exception $e) {
822
-						throw $e;
823
-					} finally {
824
-						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
825
-						$this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
826
-					}
827
-
828
-					if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
829
-						if ($this->shouldEmitHooks()) {
830
-							$this->emit_file_hooks_post($exists, $path2);
831
-						}
832
-					} elseif ($result) {
833
-						if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
834
-							\OC_Hook::emit(
835
-								Filesystem::CLASSNAME,
836
-								Filesystem::signal_post_rename,
837
-								array(
838
-									Filesystem::signal_param_oldpath => $this->getHookPath($path1),
839
-									Filesystem::signal_param_newpath => $this->getHookPath($path2)
840
-								)
841
-							);
842
-						}
843
-					}
844
-				}
845
-			} catch(\Exception $e) {
846
-				throw $e;
847
-			} finally {
848
-				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
849
-				$this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
850
-			}
851
-		}
852
-		return $result;
853
-	}
854
-
855
-	/**
856
-	 * Copy a file/folder from the source path to target path
857
-	 *
858
-	 * @param string $path1 source path
859
-	 * @param string $path2 target path
860
-	 * @param bool $preserveMtime whether to preserve mtime on the copy
861
-	 *
862
-	 * @return bool|mixed
863
-	 */
864
-	public function copy($path1, $path2, $preserveMtime = false) {
865
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
866
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
867
-		$result = false;
868
-		if (
869
-			Filesystem::isValidPath($path2)
870
-			and Filesystem::isValidPath($path1)
871
-			and !Filesystem::isFileBlacklisted($path2)
872
-		) {
873
-			$path1 = $this->getRelativePath($absolutePath1);
874
-			$path2 = $this->getRelativePath($absolutePath2);
875
-
876
-			if ($path1 == null or $path2 == null) {
877
-				return false;
878
-			}
879
-			$run = true;
880
-
881
-			$this->lockFile($path2, ILockingProvider::LOCK_SHARED);
882
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED);
883
-			$lockTypePath1 = ILockingProvider::LOCK_SHARED;
884
-			$lockTypePath2 = ILockingProvider::LOCK_SHARED;
885
-
886
-			try {
887
-
888
-				$exists = $this->file_exists($path2);
889
-				if ($this->shouldEmitHooks()) {
890
-					\OC_Hook::emit(
891
-						Filesystem::CLASSNAME,
892
-						Filesystem::signal_copy,
893
-						array(
894
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
895
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
896
-							Filesystem::signal_param_run => &$run
897
-						)
898
-					);
899
-					$this->emit_file_hooks_pre($exists, $path2, $run);
900
-				}
901
-				if ($run) {
902
-					$mount1 = $this->getMount($path1);
903
-					$mount2 = $this->getMount($path2);
904
-					$storage1 = $mount1->getStorage();
905
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
906
-					$storage2 = $mount2->getStorage();
907
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
908
-
909
-					$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
910
-					$lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
911
-
912
-					if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
913
-						if ($storage1) {
914
-							$result = $storage1->copy($internalPath1, $internalPath2);
915
-						} else {
916
-							$result = false;
917
-						}
918
-					} else {
919
-						$result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
920
-					}
921
-
922
-					$this->writeUpdate($storage2, $internalPath2);
923
-
924
-					$this->changeLock($path2, ILockingProvider::LOCK_SHARED);
925
-					$lockTypePath2 = ILockingProvider::LOCK_SHARED;
926
-
927
-					if ($this->shouldEmitHooks() && $result !== false) {
928
-						\OC_Hook::emit(
929
-							Filesystem::CLASSNAME,
930
-							Filesystem::signal_post_copy,
931
-							array(
932
-								Filesystem::signal_param_oldpath => $this->getHookPath($path1),
933
-								Filesystem::signal_param_newpath => $this->getHookPath($path2)
934
-							)
935
-						);
936
-						$this->emit_file_hooks_post($exists, $path2);
937
-					}
938
-
939
-				}
940
-			} catch (\Exception $e) {
941
-				$this->unlockFile($path2, $lockTypePath2);
942
-				$this->unlockFile($path1, $lockTypePath1);
943
-				throw $e;
944
-			}
945
-
946
-			$this->unlockFile($path2, $lockTypePath2);
947
-			$this->unlockFile($path1, $lockTypePath1);
948
-
949
-		}
950
-		return $result;
951
-	}
952
-
953
-	/**
954
-	 * @param string $path
955
-	 * @param string $mode 'r' or 'w'
956
-	 * @return resource
957
-	 */
958
-	public function fopen($path, $mode) {
959
-		$mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
960
-		$hooks = array();
961
-		switch ($mode) {
962
-			case 'r':
963
-				$hooks[] = 'read';
964
-				break;
965
-			case 'r+':
966
-			case 'w+':
967
-			case 'x+':
968
-			case 'a+':
969
-				$hooks[] = 'read';
970
-				$hooks[] = 'write';
971
-				break;
972
-			case 'w':
973
-			case 'x':
974
-			case 'a':
975
-				$hooks[] = 'write';
976
-				break;
977
-			default:
978
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
979
-		}
980
-
981
-		if ($mode !== 'r' && $mode !== 'w') {
982
-			\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');
983
-		}
984
-
985
-		return $this->basicOperation('fopen', $path, $hooks, $mode);
986
-	}
987
-
988
-	/**
989
-	 * @param string $path
990
-	 * @return bool|string
991
-	 * @throws \OCP\Files\InvalidPathException
992
-	 */
993
-	public function toTmpFile($path) {
994
-		$this->assertPathLength($path);
995
-		if (Filesystem::isValidPath($path)) {
996
-			$source = $this->fopen($path, 'r');
997
-			if ($source) {
998
-				$extension = pathinfo($path, PATHINFO_EXTENSION);
999
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1000
-				file_put_contents($tmpFile, $source);
1001
-				return $tmpFile;
1002
-			} else {
1003
-				return false;
1004
-			}
1005
-		} else {
1006
-			return false;
1007
-		}
1008
-	}
1009
-
1010
-	/**
1011
-	 * @param string $tmpFile
1012
-	 * @param string $path
1013
-	 * @return bool|mixed
1014
-	 * @throws \OCP\Files\InvalidPathException
1015
-	 */
1016
-	public function fromTmpFile($tmpFile, $path) {
1017
-		$this->assertPathLength($path);
1018
-		if (Filesystem::isValidPath($path)) {
1019
-
1020
-			// Get directory that the file is going into
1021
-			$filePath = dirname($path);
1022
-
1023
-			// Create the directories if any
1024
-			if (!$this->file_exists($filePath)) {
1025
-				$result = $this->createParentDirectories($filePath);
1026
-				if ($result === false) {
1027
-					return false;
1028
-				}
1029
-			}
1030
-
1031
-			$source = fopen($tmpFile, 'r');
1032
-			if ($source) {
1033
-				$result = $this->file_put_contents($path, $source);
1034
-				// $this->file_put_contents() might have already closed
1035
-				// the resource, so we check it, before trying to close it
1036
-				// to avoid messages in the error log.
1037
-				if (is_resource($source)) {
1038
-					fclose($source);
1039
-				}
1040
-				unlink($tmpFile);
1041
-				return $result;
1042
-			} else {
1043
-				return false;
1044
-			}
1045
-		} else {
1046
-			return false;
1047
-		}
1048
-	}
1049
-
1050
-
1051
-	/**
1052
-	 * @param string $path
1053
-	 * @return mixed
1054
-	 * @throws \OCP\Files\InvalidPathException
1055
-	 */
1056
-	public function getMimeType($path) {
1057
-		$this->assertPathLength($path);
1058
-		return $this->basicOperation('getMimeType', $path);
1059
-	}
1060
-
1061
-	/**
1062
-	 * @param string $type
1063
-	 * @param string $path
1064
-	 * @param bool $raw
1065
-	 * @return bool|null|string
1066
-	 */
1067
-	public function hash($type, $path, $raw = false) {
1068
-		$postFix = (substr($path, -1) === '/') ? '/' : '';
1069
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1070
-		if (Filesystem::isValidPath($path)) {
1071
-			$path = $this->getRelativePath($absolutePath);
1072
-			if ($path == null) {
1073
-				return false;
1074
-			}
1075
-			if ($this->shouldEmitHooks($path)) {
1076
-				\OC_Hook::emit(
1077
-					Filesystem::CLASSNAME,
1078
-					Filesystem::signal_read,
1079
-					array(Filesystem::signal_param_path => $this->getHookPath($path))
1080
-				);
1081
-			}
1082
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1083
-			if ($storage) {
1084
-				return $storage->hash($type, $internalPath, $raw);
1085
-			}
1086
-		}
1087
-		return null;
1088
-	}
1089
-
1090
-	/**
1091
-	 * @param string $path
1092
-	 * @return mixed
1093
-	 * @throws \OCP\Files\InvalidPathException
1094
-	 */
1095
-	public function free_space($path = '/') {
1096
-		$this->assertPathLength($path);
1097
-		$result = $this->basicOperation('free_space', $path);
1098
-		if ($result === null) {
1099
-			throw new InvalidPathException();
1100
-		}
1101
-		return $result;
1102
-	}
1103
-
1104
-	/**
1105
-	 * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1106
-	 *
1107
-	 * @param string $operation
1108
-	 * @param string $path
1109
-	 * @param array $hooks (optional)
1110
-	 * @param mixed $extraParam (optional)
1111
-	 * @return mixed
1112
-	 * @throws \Exception
1113
-	 *
1114
-	 * This method takes requests for basic filesystem functions (e.g. reading & writing
1115
-	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1116
-	 * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1117
-	 */
1118
-	private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1119
-		$postFix = (substr($path, -1) === '/') ? '/' : '';
1120
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1121
-		if (Filesystem::isValidPath($path)
1122
-			and !Filesystem::isFileBlacklisted($path)
1123
-		) {
1124
-			$path = $this->getRelativePath($absolutePath);
1125
-			if ($path == null) {
1126
-				return false;
1127
-			}
1128
-
1129
-			if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1130
-				// always a shared lock during pre-hooks so the hook can read the file
1131
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
1132
-			}
1133
-
1134
-			$run = $this->runHooks($hooks, $path);
1135
-			/** @var \OC\Files\Storage\Storage $storage */
1136
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1137
-			if ($run and $storage) {
1138
-				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1139
-					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1140
-				}
1141
-				try {
1142
-					if (!is_null($extraParam)) {
1143
-						$result = $storage->$operation($internalPath, $extraParam);
1144
-					} else {
1145
-						$result = $storage->$operation($internalPath);
1146
-					}
1147
-				} catch (\Exception $e) {
1148
-					if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1149
-						$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1150
-					} else if (in_array('read', $hooks)) {
1151
-						$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1152
-					}
1153
-					throw $e;
1154
-				}
1155
-
1156
-				if ($result && in_array('delete', $hooks) and $result) {
1157
-					$this->removeUpdate($storage, $internalPath);
1158
-				}
1159
-				if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1160
-					$this->writeUpdate($storage, $internalPath);
1161
-				}
1162
-				if ($result && in_array('touch', $hooks)) {
1163
-					$this->writeUpdate($storage, $internalPath, $extraParam);
1164
-				}
1165
-
1166
-				if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1167
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
1168
-				}
1169
-
1170
-				$unlockLater = false;
1171
-				if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1172
-					$unlockLater = true;
1173
-					// make sure our unlocking callback will still be called if connection is aborted
1174
-					ignore_user_abort(true);
1175
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1176
-						if (in_array('write', $hooks)) {
1177
-							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1178
-						} else if (in_array('read', $hooks)) {
1179
-							$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1180
-						}
1181
-					});
1182
-				}
1183
-
1184
-				if ($this->shouldEmitHooks($path) && $result !== false) {
1185
-					if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1186
-						$this->runHooks($hooks, $path, true);
1187
-					}
1188
-				}
1189
-
1190
-				if (!$unlockLater
1191
-					&& (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1192
-				) {
1193
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1194
-				}
1195
-				return $result;
1196
-			} else {
1197
-				$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1198
-			}
1199
-		}
1200
-		return null;
1201
-	}
1202
-
1203
-	/**
1204
-	 * get the path relative to the default root for hook usage
1205
-	 *
1206
-	 * @param string $path
1207
-	 * @return string
1208
-	 */
1209
-	private function getHookPath($path) {
1210
-		if (!Filesystem::getView()) {
1211
-			return $path;
1212
-		}
1213
-		return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1214
-	}
1215
-
1216
-	private function shouldEmitHooks($path = '') {
1217
-		if ($path && Cache\Scanner::isPartialFile($path)) {
1218
-			return false;
1219
-		}
1220
-		if (!Filesystem::$loaded) {
1221
-			return false;
1222
-		}
1223
-		$defaultRoot = Filesystem::getRoot();
1224
-		if ($defaultRoot === null) {
1225
-			return false;
1226
-		}
1227
-		if ($this->fakeRoot === $defaultRoot) {
1228
-			return true;
1229
-		}
1230
-		$fullPath = $this->getAbsolutePath($path);
1231
-
1232
-		if ($fullPath === $defaultRoot) {
1233
-			return true;
1234
-		}
1235
-
1236
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1237
-	}
1238
-
1239
-	/**
1240
-	 * @param string[] $hooks
1241
-	 * @param string $path
1242
-	 * @param bool $post
1243
-	 * @return bool
1244
-	 */
1245
-	private function runHooks($hooks, $path, $post = false) {
1246
-		$relativePath = $path;
1247
-		$path = $this->getHookPath($path);
1248
-		$prefix = $post ? 'post_' : '';
1249
-		$run = true;
1250
-		if ($this->shouldEmitHooks($relativePath)) {
1251
-			foreach ($hooks as $hook) {
1252
-				if ($hook != 'read') {
1253
-					\OC_Hook::emit(
1254
-						Filesystem::CLASSNAME,
1255
-						$prefix . $hook,
1256
-						array(
1257
-							Filesystem::signal_param_run => &$run,
1258
-							Filesystem::signal_param_path => $path
1259
-						)
1260
-					);
1261
-				} elseif (!$post) {
1262
-					\OC_Hook::emit(
1263
-						Filesystem::CLASSNAME,
1264
-						$prefix . $hook,
1265
-						array(
1266
-							Filesystem::signal_param_path => $path
1267
-						)
1268
-					);
1269
-				}
1270
-			}
1271
-		}
1272
-		return $run;
1273
-	}
1274
-
1275
-	/**
1276
-	 * check if a file or folder has been updated since $time
1277
-	 *
1278
-	 * @param string $path
1279
-	 * @param int $time
1280
-	 * @return bool
1281
-	 */
1282
-	public function hasUpdated($path, $time) {
1283
-		return $this->basicOperation('hasUpdated', $path, array(), $time);
1284
-	}
1285
-
1286
-	/**
1287
-	 * @param string $ownerId
1288
-	 * @return \OC\User\User
1289
-	 */
1290
-	private function getUserObjectForOwner($ownerId) {
1291
-		$owner = $this->userManager->get($ownerId);
1292
-		if ($owner instanceof IUser) {
1293
-			return $owner;
1294
-		} else {
1295
-			return new User($ownerId, null);
1296
-		}
1297
-	}
1298
-
1299
-	/**
1300
-	 * Get file info from cache
1301
-	 *
1302
-	 * If the file is not in cached it will be scanned
1303
-	 * If the file has changed on storage the cache will be updated
1304
-	 *
1305
-	 * @param \OC\Files\Storage\Storage $storage
1306
-	 * @param string $internalPath
1307
-	 * @param string $relativePath
1308
-	 * @return ICacheEntry|bool
1309
-	 */
1310
-	private function getCacheEntry($storage, $internalPath, $relativePath) {
1311
-		$cache = $storage->getCache($internalPath);
1312
-		$data = $cache->get($internalPath);
1313
-		$watcher = $storage->getWatcher($internalPath);
1314
-
1315
-		try {
1316
-			// if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1317
-			if (!$data || $data['size'] === -1) {
1318
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1319
-				if (!$storage->file_exists($internalPath)) {
1320
-					$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1321
-					return false;
1322
-				}
1323
-				$scanner = $storage->getScanner($internalPath);
1324
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1325
-				$data = $cache->get($internalPath);
1326
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1327
-			} else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1328
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1329
-				$watcher->update($internalPath, $data);
1330
-				$storage->getPropagator()->propagateChange($internalPath, time());
1331
-				$data = $cache->get($internalPath);
1332
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1333
-			}
1334
-		} catch (LockedException $e) {
1335
-			// if the file is locked we just use the old cache info
1336
-		}
1337
-
1338
-		return $data;
1339
-	}
1340
-
1341
-	/**
1342
-	 * get the filesystem info
1343
-	 *
1344
-	 * @param string $path
1345
-	 * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1346
-	 * 'ext' to add only ext storage mount point sizes. Defaults to true.
1347
-	 * defaults to true
1348
-	 * @return \OC\Files\FileInfo|false False if file does not exist
1349
-	 */
1350
-	public function getFileInfo($path, $includeMountPoints = true) {
1351
-		$this->assertPathLength($path);
1352
-		if (!Filesystem::isValidPath($path)) {
1353
-			return false;
1354
-		}
1355
-		if (Cache\Scanner::isPartialFile($path)) {
1356
-			return $this->getPartFileInfo($path);
1357
-		}
1358
-		$relativePath = $path;
1359
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1360
-
1361
-		$mount = Filesystem::getMountManager()->find($path);
1362
-		if (!$mount) {
1363
-			return false;
1364
-		}
1365
-		$storage = $mount->getStorage();
1366
-		$internalPath = $mount->getInternalPath($path);
1367
-		if ($storage) {
1368
-			$data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1369
-
1370
-			if (!$data instanceof ICacheEntry) {
1371
-				return false;
1372
-			}
1373
-
1374
-			if ($mount instanceof MoveableMount && $internalPath === '') {
1375
-				$data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1376
-			}
1377
-
1378
-			$owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1379
-			$info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1380
-
1381
-			if ($data and isset($data['fileid'])) {
1382
-				if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1383
-					//add the sizes of other mount points to the folder
1384
-					$extOnly = ($includeMountPoints === 'ext');
1385
-					$mounts = Filesystem::getMountManager()->findIn($path);
1386
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1387
-						$subStorage = $mount->getStorage();
1388
-						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1389
-					}));
1390
-				}
1391
-			}
1392
-
1393
-			return $info;
1394
-		}
1395
-
1396
-		return false;
1397
-	}
1398
-
1399
-	/**
1400
-	 * get the content of a directory
1401
-	 *
1402
-	 * @param string $directory path under datadirectory
1403
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1404
-	 * @return FileInfo[]
1405
-	 */
1406
-	public function getDirectoryContent($directory, $mimetype_filter = '') {
1407
-		$this->assertPathLength($directory);
1408
-		if (!Filesystem::isValidPath($directory)) {
1409
-			return [];
1410
-		}
1411
-		$path = $this->getAbsolutePath($directory);
1412
-		$path = Filesystem::normalizePath($path);
1413
-		$mount = $this->getMount($directory);
1414
-		if (!$mount) {
1415
-			return [];
1416
-		}
1417
-		$storage = $mount->getStorage();
1418
-		$internalPath = $mount->getInternalPath($path);
1419
-		if ($storage) {
1420
-			$cache = $storage->getCache($internalPath);
1421
-			$user = \OC_User::getUser();
1422
-
1423
-			$data = $this->getCacheEntry($storage, $internalPath, $directory);
1424
-
1425
-			if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1426
-				return [];
1427
-			}
1428
-
1429
-			$folderId = $data['fileid'];
1430
-			$contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1431
-
1432
-			$sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1433
-			/**
1434
-			 * @var \OC\Files\FileInfo[] $files
1435
-			 */
1436
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1437
-				if ($sharingDisabled) {
1438
-					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1439
-				}
1440
-				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1441
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1442
-			}, $contents);
1443
-
1444
-			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1445
-			$mounts = Filesystem::getMountManager()->findIn($path);
1446
-			$dirLength = strlen($path);
1447
-			foreach ($mounts as $mount) {
1448
-				$mountPoint = $mount->getMountPoint();
1449
-				$subStorage = $mount->getStorage();
1450
-				if ($subStorage) {
1451
-					$subCache = $subStorage->getCache('');
1452
-
1453
-					$rootEntry = $subCache->get('');
1454
-					if (!$rootEntry) {
1455
-						$subScanner = $subStorage->getScanner('');
1456
-						try {
1457
-							$subScanner->scanFile('');
1458
-						} catch (\OCP\Files\StorageNotAvailableException $e) {
1459
-							continue;
1460
-						} catch (\OCP\Files\StorageInvalidException $e) {
1461
-							continue;
1462
-						} catch (\Exception $e) {
1463
-							// sometimes when the storage is not available it can be any exception
1464
-							\OC::$server->getLogger()->logException($e, [
1465
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1466
-								'level' => \OCP\Util::ERROR,
1467
-								'app' => 'lib',
1468
-							]);
1469
-							continue;
1470
-						}
1471
-						$rootEntry = $subCache->get('');
1472
-					}
1473
-
1474
-					if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1475
-						$relativePath = trim(substr($mountPoint, $dirLength), '/');
1476
-						if ($pos = strpos($relativePath, '/')) {
1477
-							//mountpoint inside subfolder add size to the correct folder
1478
-							$entryName = substr($relativePath, 0, $pos);
1479
-							foreach ($files as &$entry) {
1480
-								if ($entry->getName() === $entryName) {
1481
-									$entry->addSubEntry($rootEntry, $mountPoint);
1482
-								}
1483
-							}
1484
-						} else { //mountpoint in this folder, add an entry for it
1485
-							$rootEntry['name'] = $relativePath;
1486
-							$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1487
-							$permissions = $rootEntry['permissions'];
1488
-							// do not allow renaming/deleting the mount point if they are not shared files/folders
1489
-							// for shared files/folders we use the permissions given by the owner
1490
-							if ($mount instanceof MoveableMount) {
1491
-								$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1492
-							} else {
1493
-								$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1494
-							}
1495
-
1496
-							//remove any existing entry with the same name
1497
-							foreach ($files as $i => $file) {
1498
-								if ($file['name'] === $rootEntry['name']) {
1499
-									unset($files[$i]);
1500
-									break;
1501
-								}
1502
-							}
1503
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1504
-
1505
-							// if sharing was disabled for the user we remove the share permissions
1506
-							if (\OCP\Util::isSharingDisabledForUser()) {
1507
-								$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1508
-							}
1509
-
1510
-							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1511
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1512
-						}
1513
-					}
1514
-				}
1515
-			}
1516
-
1517
-			if ($mimetype_filter) {
1518
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1519
-					if (strpos($mimetype_filter, '/')) {
1520
-						return $file->getMimetype() === $mimetype_filter;
1521
-					} else {
1522
-						return $file->getMimePart() === $mimetype_filter;
1523
-					}
1524
-				});
1525
-			}
1526
-
1527
-			return $files;
1528
-		} else {
1529
-			return [];
1530
-		}
1531
-	}
1532
-
1533
-	/**
1534
-	 * change file metadata
1535
-	 *
1536
-	 * @param string $path
1537
-	 * @param array|\OCP\Files\FileInfo $data
1538
-	 * @return int
1539
-	 *
1540
-	 * returns the fileid of the updated file
1541
-	 */
1542
-	public function putFileInfo($path, $data) {
1543
-		$this->assertPathLength($path);
1544
-		if ($data instanceof FileInfo) {
1545
-			$data = $data->getData();
1546
-		}
1547
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1548
-		/**
1549
-		 * @var \OC\Files\Storage\Storage $storage
1550
-		 * @var string $internalPath
1551
-		 */
1552
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
1553
-		if ($storage) {
1554
-			$cache = $storage->getCache($path);
1555
-
1556
-			if (!$cache->inCache($internalPath)) {
1557
-				$scanner = $storage->getScanner($internalPath);
1558
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1559
-			}
1560
-
1561
-			return $cache->put($internalPath, $data);
1562
-		} else {
1563
-			return -1;
1564
-		}
1565
-	}
1566
-
1567
-	/**
1568
-	 * search for files with the name matching $query
1569
-	 *
1570
-	 * @param string $query
1571
-	 * @return FileInfo[]
1572
-	 */
1573
-	public function search($query) {
1574
-		return $this->searchCommon('search', array('%' . $query . '%'));
1575
-	}
1576
-
1577
-	/**
1578
-	 * search for files with the name matching $query
1579
-	 *
1580
-	 * @param string $query
1581
-	 * @return FileInfo[]
1582
-	 */
1583
-	public function searchRaw($query) {
1584
-		return $this->searchCommon('search', array($query));
1585
-	}
1586
-
1587
-	/**
1588
-	 * search for files by mimetype
1589
-	 *
1590
-	 * @param string $mimetype
1591
-	 * @return FileInfo[]
1592
-	 */
1593
-	public function searchByMime($mimetype) {
1594
-		return $this->searchCommon('searchByMime', array($mimetype));
1595
-	}
1596
-
1597
-	/**
1598
-	 * search for files by tag
1599
-	 *
1600
-	 * @param string|int $tag name or tag id
1601
-	 * @param string $userId owner of the tags
1602
-	 * @return FileInfo[]
1603
-	 */
1604
-	public function searchByTag($tag, $userId) {
1605
-		return $this->searchCommon('searchByTag', array($tag, $userId));
1606
-	}
1607
-
1608
-	/**
1609
-	 * @param string $method cache method
1610
-	 * @param array $args
1611
-	 * @return FileInfo[]
1612
-	 */
1613
-	private function searchCommon($method, $args) {
1614
-		$files = array();
1615
-		$rootLength = strlen($this->fakeRoot);
1616
-
1617
-		$mount = $this->getMount('');
1618
-		$mountPoint = $mount->getMountPoint();
1619
-		$storage = $mount->getStorage();
1620
-		if ($storage) {
1621
-			$cache = $storage->getCache('');
1622
-
1623
-			$results = call_user_func_array(array($cache, $method), $args);
1624
-			foreach ($results as $result) {
1625
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1626
-					$internalPath = $result['path'];
1627
-					$path = $mountPoint . $result['path'];
1628
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1629
-					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1630
-					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1631
-				}
1632
-			}
1633
-
1634
-			$mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1635
-			foreach ($mounts as $mount) {
1636
-				$mountPoint = $mount->getMountPoint();
1637
-				$storage = $mount->getStorage();
1638
-				if ($storage) {
1639
-					$cache = $storage->getCache('');
1640
-
1641
-					$relativeMountPoint = substr($mountPoint, $rootLength);
1642
-					$results = call_user_func_array(array($cache, $method), $args);
1643
-					if ($results) {
1644
-						foreach ($results as $result) {
1645
-							$internalPath = $result['path'];
1646
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1647
-							$path = rtrim($mountPoint . $internalPath, '/');
1648
-							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1649
-							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1650
-						}
1651
-					}
1652
-				}
1653
-			}
1654
-		}
1655
-		return $files;
1656
-	}
1657
-
1658
-	/**
1659
-	 * Get the owner for a file or folder
1660
-	 *
1661
-	 * @param string $path
1662
-	 * @return string the user id of the owner
1663
-	 * @throws NotFoundException
1664
-	 */
1665
-	public function getOwner($path) {
1666
-		$info = $this->getFileInfo($path);
1667
-		if (!$info) {
1668
-			throw new NotFoundException($path . ' not found while trying to get owner');
1669
-		}
1670
-		return $info->getOwner()->getUID();
1671
-	}
1672
-
1673
-	/**
1674
-	 * get the ETag for a file or folder
1675
-	 *
1676
-	 * @param string $path
1677
-	 * @return string
1678
-	 */
1679
-	public function getETag($path) {
1680
-		/**
1681
-		 * @var Storage\Storage $storage
1682
-		 * @var string $internalPath
1683
-		 */
1684
-		list($storage, $internalPath) = $this->resolvePath($path);
1685
-		if ($storage) {
1686
-			return $storage->getETag($internalPath);
1687
-		} else {
1688
-			return null;
1689
-		}
1690
-	}
1691
-
1692
-	/**
1693
-	 * Get the path of a file by id, relative to the view
1694
-	 *
1695
-	 * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1696
-	 *
1697
-	 * @param int $id
1698
-	 * @throws NotFoundException
1699
-	 * @return string
1700
-	 */
1701
-	public function getPath($id) {
1702
-		$id = (int)$id;
1703
-		$manager = Filesystem::getMountManager();
1704
-		$mounts = $manager->findIn($this->fakeRoot);
1705
-		$mounts[] = $manager->find($this->fakeRoot);
1706
-		// reverse the array so we start with the storage this view is in
1707
-		// which is the most likely to contain the file we're looking for
1708
-		$mounts = array_reverse($mounts);
1709
-		foreach ($mounts as $mount) {
1710
-			/**
1711
-			 * @var \OC\Files\Mount\MountPoint $mount
1712
-			 */
1713
-			if ($mount->getStorage()) {
1714
-				$cache = $mount->getStorage()->getCache();
1715
-				$internalPath = $cache->getPathById($id);
1716
-				if (is_string($internalPath)) {
1717
-					$fullPath = $mount->getMountPoint() . $internalPath;
1718
-					if (!is_null($path = $this->getRelativePath($fullPath))) {
1719
-						return $path;
1720
-					}
1721
-				}
1722
-			}
1723
-		}
1724
-		throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1725
-	}
1726
-
1727
-	/**
1728
-	 * @param string $path
1729
-	 * @throws InvalidPathException
1730
-	 */
1731
-	private function assertPathLength($path) {
1732
-		$maxLen = min(PHP_MAXPATHLEN, 4000);
1733
-		// Check for the string length - performed using isset() instead of strlen()
1734
-		// because isset() is about 5x-40x faster.
1735
-		if (isset($path[$maxLen])) {
1736
-			$pathLen = strlen($path);
1737
-			throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1738
-		}
1739
-	}
1740
-
1741
-	/**
1742
-	 * check if it is allowed to move a mount point to a given target.
1743
-	 * It is not allowed to move a mount point into a different mount point or
1744
-	 * into an already shared folder
1745
-	 *
1746
-	 * @param string $target path
1747
-	 * @return boolean
1748
-	 */
1749
-	private function isTargetAllowed($target) {
1750
-
1751
-		list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1752
-		if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1753
-			\OCP\Util::writeLog('files',
1754
-				'It is not allowed to move one mount point into another one',
1755
-				\OCP\Util::DEBUG);
1756
-			return false;
1757
-		}
1758
-
1759
-		// note: cannot use the view because the target is already locked
1760
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1761
-		if ($fileId === -1) {
1762
-			// target might not exist, need to check parent instead
1763
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1764
-		}
1765
-
1766
-		// check if any of the parents were shared by the current owner (include collections)
1767
-		$shares = \OCP\Share::getItemShared(
1768
-			'folder',
1769
-			$fileId,
1770
-			\OCP\Share::FORMAT_NONE,
1771
-			null,
1772
-			true
1773
-		);
1774
-
1775
-		if (count($shares) > 0) {
1776
-			\OCP\Util::writeLog('files',
1777
-				'It is not allowed to move one mount point into a shared folder',
1778
-				\OCP\Util::DEBUG);
1779
-			return false;
1780
-		}
1781
-
1782
-		return true;
1783
-	}
1784
-
1785
-	/**
1786
-	 * Get a fileinfo object for files that are ignored in the cache (part files)
1787
-	 *
1788
-	 * @param string $path
1789
-	 * @return \OCP\Files\FileInfo
1790
-	 */
1791
-	private function getPartFileInfo($path) {
1792
-		$mount = $this->getMount($path);
1793
-		$storage = $mount->getStorage();
1794
-		$internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1795
-		$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1796
-		return new FileInfo(
1797
-			$this->getAbsolutePath($path),
1798
-			$storage,
1799
-			$internalPath,
1800
-			[
1801
-				'fileid' => null,
1802
-				'mimetype' => $storage->getMimeType($internalPath),
1803
-				'name' => basename($path),
1804
-				'etag' => null,
1805
-				'size' => $storage->filesize($internalPath),
1806
-				'mtime' => $storage->filemtime($internalPath),
1807
-				'encrypted' => false,
1808
-				'permissions' => \OCP\Constants::PERMISSION_ALL
1809
-			],
1810
-			$mount,
1811
-			$owner
1812
-		);
1813
-	}
1814
-
1815
-	/**
1816
-	 * @param string $path
1817
-	 * @param string $fileName
1818
-	 * @throws InvalidPathException
1819
-	 */
1820
-	public function verifyPath($path, $fileName) {
1821
-		try {
1822
-			/** @type \OCP\Files\Storage $storage */
1823
-			list($storage, $internalPath) = $this->resolvePath($path);
1824
-			$storage->verifyPath($internalPath, $fileName);
1825
-		} catch (ReservedWordException $ex) {
1826
-			$l = \OC::$server->getL10N('lib');
1827
-			throw new InvalidPathException($l->t('File name is a reserved word'));
1828
-		} catch (InvalidCharacterInPathException $ex) {
1829
-			$l = \OC::$server->getL10N('lib');
1830
-			throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1831
-		} catch (FileNameTooLongException $ex) {
1832
-			$l = \OC::$server->getL10N('lib');
1833
-			throw new InvalidPathException($l->t('File name is too long'));
1834
-		} catch (InvalidDirectoryException $ex) {
1835
-			$l = \OC::$server->getL10N('lib');
1836
-			throw new InvalidPathException($l->t('Dot files are not allowed'));
1837
-		} catch (EmptyFileNameException $ex) {
1838
-			$l = \OC::$server->getL10N('lib');
1839
-			throw new InvalidPathException($l->t('Empty filename is not allowed'));
1840
-		}
1841
-	}
1842
-
1843
-	/**
1844
-	 * get all parent folders of $path
1845
-	 *
1846
-	 * @param string $path
1847
-	 * @return string[]
1848
-	 */
1849
-	private function getParents($path) {
1850
-		$path = trim($path, '/');
1851
-		if (!$path) {
1852
-			return [];
1853
-		}
1854
-
1855
-		$parts = explode('/', $path);
1856
-
1857
-		// remove the single file
1858
-		array_pop($parts);
1859
-		$result = array('/');
1860
-		$resultPath = '';
1861
-		foreach ($parts as $part) {
1862
-			if ($part) {
1863
-				$resultPath .= '/' . $part;
1864
-				$result[] = $resultPath;
1865
-			}
1866
-		}
1867
-		return $result;
1868
-	}
1869
-
1870
-	/**
1871
-	 * Returns the mount point for which to lock
1872
-	 *
1873
-	 * @param string $absolutePath absolute path
1874
-	 * @param bool $useParentMount true to return parent mount instead of whatever
1875
-	 * is mounted directly on the given path, false otherwise
1876
-	 * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1877
-	 */
1878
-	private function getMountForLock($absolutePath, $useParentMount = false) {
1879
-		$results = [];
1880
-		$mount = Filesystem::getMountManager()->find($absolutePath);
1881
-		if (!$mount) {
1882
-			return $results;
1883
-		}
1884
-
1885
-		if ($useParentMount) {
1886
-			// find out if something is mounted directly on the path
1887
-			$internalPath = $mount->getInternalPath($absolutePath);
1888
-			if ($internalPath === '') {
1889
-				// resolve the parent mount instead
1890
-				$mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1891
-			}
1892
-		}
1893
-
1894
-		return $mount;
1895
-	}
1896
-
1897
-	/**
1898
-	 * Lock the given path
1899
-	 *
1900
-	 * @param string $path the path of the file to lock, relative to the view
1901
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1902
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1903
-	 *
1904
-	 * @return bool False if the path is excluded from locking, true otherwise
1905
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1906
-	 */
1907
-	private function lockPath($path, $type, $lockMountPoint = false) {
1908
-		$absolutePath = $this->getAbsolutePath($path);
1909
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1910
-		if (!$this->shouldLockFile($absolutePath)) {
1911
-			return false;
1912
-		}
1913
-
1914
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1915
-		if ($mount) {
1916
-			try {
1917
-				$storage = $mount->getStorage();
1918
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1919
-					$storage->acquireLock(
1920
-						$mount->getInternalPath($absolutePath),
1921
-						$type,
1922
-						$this->lockingProvider
1923
-					);
1924
-				}
1925
-			} catch (\OCP\Lock\LockedException $e) {
1926
-				// rethrow with the a human-readable path
1927
-				throw new \OCP\Lock\LockedException(
1928
-					$this->getPathRelativeToFiles($absolutePath),
1929
-					$e
1930
-				);
1931
-			}
1932
-		}
1933
-
1934
-		return true;
1935
-	}
1936
-
1937
-	/**
1938
-	 * Change the lock type
1939
-	 *
1940
-	 * @param string $path the path of the file to lock, relative to the view
1941
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1942
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1943
-	 *
1944
-	 * @return bool False if the path is excluded from locking, true otherwise
1945
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1946
-	 */
1947
-	public function changeLock($path, $type, $lockMountPoint = false) {
1948
-		$path = Filesystem::normalizePath($path);
1949
-		$absolutePath = $this->getAbsolutePath($path);
1950
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1951
-		if (!$this->shouldLockFile($absolutePath)) {
1952
-			return false;
1953
-		}
1954
-
1955
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1956
-		if ($mount) {
1957
-			try {
1958
-				$storage = $mount->getStorage();
1959
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1960
-					$storage->changeLock(
1961
-						$mount->getInternalPath($absolutePath),
1962
-						$type,
1963
-						$this->lockingProvider
1964
-					);
1965
-				}
1966
-			} catch (\OCP\Lock\LockedException $e) {
1967
-				try {
1968
-					// rethrow with the a human-readable path
1969
-					throw new \OCP\Lock\LockedException(
1970
-						$this->getPathRelativeToFiles($absolutePath),
1971
-						$e
1972
-					);
1973
-				} catch (\InvalidArgumentException $e) {
1974
-					throw new \OCP\Lock\LockedException(
1975
-						$absolutePath,
1976
-						$e
1977
-					);
1978
-				}
1979
-			}
1980
-		}
1981
-
1982
-		return true;
1983
-	}
1984
-
1985
-	/**
1986
-	 * Unlock the given path
1987
-	 *
1988
-	 * @param string $path the path of the file to unlock, relative to the view
1989
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1990
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1991
-	 *
1992
-	 * @return bool False if the path is excluded from locking, true otherwise
1993
-	 */
1994
-	private function unlockPath($path, $type, $lockMountPoint = false) {
1995
-		$absolutePath = $this->getAbsolutePath($path);
1996
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1997
-		if (!$this->shouldLockFile($absolutePath)) {
1998
-			return false;
1999
-		}
2000
-
2001
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2002
-		if ($mount) {
2003
-			$storage = $mount->getStorage();
2004
-			if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2005
-				$storage->releaseLock(
2006
-					$mount->getInternalPath($absolutePath),
2007
-					$type,
2008
-					$this->lockingProvider
2009
-				);
2010
-			}
2011
-		}
2012
-
2013
-		return true;
2014
-	}
2015
-
2016
-	/**
2017
-	 * Lock a path and all its parents up to the root of the view
2018
-	 *
2019
-	 * @param string $path the path of the file to lock relative to the view
2020
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2021
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2022
-	 *
2023
-	 * @return bool False if the path is excluded from locking, true otherwise
2024
-	 */
2025
-	public function lockFile($path, $type, $lockMountPoint = false) {
2026
-		$absolutePath = $this->getAbsolutePath($path);
2027
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2028
-		if (!$this->shouldLockFile($absolutePath)) {
2029
-			return false;
2030
-		}
2031
-
2032
-		$this->lockPath($path, $type, $lockMountPoint);
2033
-
2034
-		$parents = $this->getParents($path);
2035
-		foreach ($parents as $parent) {
2036
-			$this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2037
-		}
2038
-
2039
-		return true;
2040
-	}
2041
-
2042
-	/**
2043
-	 * Unlock a path and all its parents up to the root of the view
2044
-	 *
2045
-	 * @param string $path the path of the file to lock relative to the view
2046
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2047
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2048
-	 *
2049
-	 * @return bool False if the path is excluded from locking, true otherwise
2050
-	 */
2051
-	public function unlockFile($path, $type, $lockMountPoint = false) {
2052
-		$absolutePath = $this->getAbsolutePath($path);
2053
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2054
-		if (!$this->shouldLockFile($absolutePath)) {
2055
-			return false;
2056
-		}
2057
-
2058
-		$this->unlockPath($path, $type, $lockMountPoint);
2059
-
2060
-		$parents = $this->getParents($path);
2061
-		foreach ($parents as $parent) {
2062
-			$this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2063
-		}
2064
-
2065
-		return true;
2066
-	}
2067
-
2068
-	/**
2069
-	 * Only lock files in data/user/files/
2070
-	 *
2071
-	 * @param string $path Absolute path to the file/folder we try to (un)lock
2072
-	 * @return bool
2073
-	 */
2074
-	protected function shouldLockFile($path) {
2075
-		$path = Filesystem::normalizePath($path);
2076
-
2077
-		$pathSegments = explode('/', $path);
2078
-		if (isset($pathSegments[2])) {
2079
-			// E.g.: /username/files/path-to-file
2080
-			return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2081
-		}
2082
-
2083
-		return strpos($path, '/appdata_') !== 0;
2084
-	}
2085
-
2086
-	/**
2087
-	 * Shortens the given absolute path to be relative to
2088
-	 * "$user/files".
2089
-	 *
2090
-	 * @param string $absolutePath absolute path which is under "files"
2091
-	 *
2092
-	 * @return string path relative to "files" with trimmed slashes or null
2093
-	 * if the path was NOT relative to files
2094
-	 *
2095
-	 * @throws \InvalidArgumentException if the given path was not under "files"
2096
-	 * @since 8.1.0
2097
-	 */
2098
-	public function getPathRelativeToFiles($absolutePath) {
2099
-		$path = Filesystem::normalizePath($absolutePath);
2100
-		$parts = explode('/', trim($path, '/'), 3);
2101
-		// "$user", "files", "path/to/dir"
2102
-		if (!isset($parts[1]) || $parts[1] !== 'files') {
2103
-			$this->logger->error(
2104
-				'$absolutePath must be relative to "files", value is "%s"',
2105
-				[
2106
-					$absolutePath
2107
-				]
2108
-			);
2109
-			throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2110
-		}
2111
-		if (isset($parts[2])) {
2112
-			return $parts[2];
2113
-		}
2114
-		return '';
2115
-	}
2116
-
2117
-	/**
2118
-	 * @param string $filename
2119
-	 * @return array
2120
-	 * @throws \OC\User\NoUserException
2121
-	 * @throws NotFoundException
2122
-	 */
2123
-	public function getUidAndFilename($filename) {
2124
-		$info = $this->getFileInfo($filename);
2125
-		if (!$info instanceof \OCP\Files\FileInfo) {
2126
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2127
-		}
2128
-		$uid = $info->getOwner()->getUID();
2129
-		if ($uid != \OCP\User::getUser()) {
2130
-			Filesystem::initMountPoints($uid);
2131
-			$ownerView = new View('/' . $uid . '/files');
2132
-			try {
2133
-				$filename = $ownerView->getPath($info['fileid']);
2134
-			} catch (NotFoundException $e) {
2135
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2136
-			}
2137
-		}
2138
-		return [$uid, $filename];
2139
-	}
2140
-
2141
-	/**
2142
-	 * Creates parent non-existing folders
2143
-	 *
2144
-	 * @param string $filePath
2145
-	 * @return bool
2146
-	 */
2147
-	private function createParentDirectories($filePath) {
2148
-		$directoryParts = explode('/', $filePath);
2149
-		$directoryParts = array_filter($directoryParts);
2150
-		foreach ($directoryParts as $key => $part) {
2151
-			$currentPathElements = array_slice($directoryParts, 0, $key);
2152
-			$currentPath = '/' . implode('/', $currentPathElements);
2153
-			if ($this->is_file($currentPath)) {
2154
-				return false;
2155
-			}
2156
-			if (!$this->file_exists($currentPath)) {
2157
-				$this->mkdir($currentPath);
2158
-			}
2159
-		}
2160
-
2161
-		return true;
2162
-	}
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
+            return $this->filesize($path);
430
+        }
431
+        return false;
432
+    }
433
+
434
+    /**
435
+     * @param string $path
436
+     * @param int $from
437
+     * @param int $to
438
+     * @return bool|mixed
439
+     * @throws \OCP\Files\InvalidPathException
440
+     * @throws \OCP\Files\UnseekableException
441
+     */
442
+    public function readfilePart($path, $from, $to) {
443
+        $this->assertPathLength($path);
444
+        @ob_end_clean();
445
+        $handle = $this->fopen($path, 'rb');
446
+        if ($handle) {
447
+            $chunkSize = 8192; // 8 kB chunks
448
+            $startReading = true;
449
+
450
+            if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
451
+                // forward file handle via chunked fread because fseek seem to have failed
452
+
453
+                $end = $from + 1;
454
+                while (!feof($handle) && ftell($handle) < $end) {
455
+                    $len = $from - ftell($handle);
456
+                    if ($len > $chunkSize) {
457
+                        $len = $chunkSize;
458
+                    }
459
+                    $result = fread($handle, $len);
460
+
461
+                    if ($result === false) {
462
+                        $startReading = false;
463
+                        break;
464
+                    }
465
+                }
466
+            }
467
+
468
+            if ($startReading) {
469
+                $end = $to + 1;
470
+                while (!feof($handle) && ftell($handle) < $end) {
471
+                    $len = $end - ftell($handle);
472
+                    if ($len > $chunkSize) {
473
+                        $len = $chunkSize;
474
+                    }
475
+                    echo fread($handle, $len);
476
+                    flush();
477
+                }
478
+                return ftell($handle) - $from;
479
+            }
480
+
481
+            throw new \OCP\Files\UnseekableException('fseek error');
482
+        }
483
+        return false;
484
+    }
485
+
486
+    /**
487
+     * @param string $path
488
+     * @return mixed
489
+     */
490
+    public function isCreatable($path) {
491
+        return $this->basicOperation('isCreatable', $path);
492
+    }
493
+
494
+    /**
495
+     * @param string $path
496
+     * @return mixed
497
+     */
498
+    public function isReadable($path) {
499
+        return $this->basicOperation('isReadable', $path);
500
+    }
501
+
502
+    /**
503
+     * @param string $path
504
+     * @return mixed
505
+     */
506
+    public function isUpdatable($path) {
507
+        return $this->basicOperation('isUpdatable', $path);
508
+    }
509
+
510
+    /**
511
+     * @param string $path
512
+     * @return bool|mixed
513
+     */
514
+    public function isDeletable($path) {
515
+        $absolutePath = $this->getAbsolutePath($path);
516
+        $mount = Filesystem::getMountManager()->find($absolutePath);
517
+        if ($mount->getInternalPath($absolutePath) === '') {
518
+            return $mount instanceof MoveableMount;
519
+        }
520
+        return $this->basicOperation('isDeletable', $path);
521
+    }
522
+
523
+    /**
524
+     * @param string $path
525
+     * @return mixed
526
+     */
527
+    public function isSharable($path) {
528
+        return $this->basicOperation('isSharable', $path);
529
+    }
530
+
531
+    /**
532
+     * @param string $path
533
+     * @return bool|mixed
534
+     */
535
+    public function file_exists($path) {
536
+        if ($path == '/') {
537
+            return true;
538
+        }
539
+        return $this->basicOperation('file_exists', $path);
540
+    }
541
+
542
+    /**
543
+     * @param string $path
544
+     * @return mixed
545
+     */
546
+    public function filemtime($path) {
547
+        return $this->basicOperation('filemtime', $path);
548
+    }
549
+
550
+    /**
551
+     * @param string $path
552
+     * @param int|string $mtime
553
+     * @return bool
554
+     */
555
+    public function touch($path, $mtime = null) {
556
+        if (!is_null($mtime) and !is_numeric($mtime)) {
557
+            $mtime = strtotime($mtime);
558
+        }
559
+
560
+        $hooks = array('touch');
561
+
562
+        if (!$this->file_exists($path)) {
563
+            $hooks[] = 'create';
564
+            $hooks[] = 'write';
565
+        }
566
+        $result = $this->basicOperation('touch', $path, $hooks, $mtime);
567
+        if (!$result) {
568
+            // If create file fails because of permissions on external storage like SMB folders,
569
+            // check file exists and return false if not.
570
+            if (!$this->file_exists($path)) {
571
+                return false;
572
+            }
573
+            if (is_null($mtime)) {
574
+                $mtime = time();
575
+            }
576
+            //if native touch fails, we emulate it by changing the mtime in the cache
577
+            $this->putFileInfo($path, array('mtime' => floor($mtime)));
578
+        }
579
+        return true;
580
+    }
581
+
582
+    /**
583
+     * @param string $path
584
+     * @return mixed
585
+     */
586
+    public function file_get_contents($path) {
587
+        return $this->basicOperation('file_get_contents', $path, array('read'));
588
+    }
589
+
590
+    /**
591
+     * @param bool $exists
592
+     * @param string $path
593
+     * @param bool $run
594
+     */
595
+    protected function emit_file_hooks_pre($exists, $path, &$run) {
596
+        if (!$exists) {
597
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
598
+                Filesystem::signal_param_path => $this->getHookPath($path),
599
+                Filesystem::signal_param_run => &$run,
600
+            ));
601
+        } else {
602
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
603
+                Filesystem::signal_param_path => $this->getHookPath($path),
604
+                Filesystem::signal_param_run => &$run,
605
+            ));
606
+        }
607
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
608
+            Filesystem::signal_param_path => $this->getHookPath($path),
609
+            Filesystem::signal_param_run => &$run,
610
+        ));
611
+    }
612
+
613
+    /**
614
+     * @param bool $exists
615
+     * @param string $path
616
+     */
617
+    protected function emit_file_hooks_post($exists, $path) {
618
+        if (!$exists) {
619
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
620
+                Filesystem::signal_param_path => $this->getHookPath($path),
621
+            ));
622
+        } else {
623
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
624
+                Filesystem::signal_param_path => $this->getHookPath($path),
625
+            ));
626
+        }
627
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
628
+            Filesystem::signal_param_path => $this->getHookPath($path),
629
+        ));
630
+    }
631
+
632
+    /**
633
+     * @param string $path
634
+     * @param mixed $data
635
+     * @return bool|mixed
636
+     * @throws \Exception
637
+     */
638
+    public function file_put_contents($path, $data) {
639
+        if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
640
+            $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
641
+            if (Filesystem::isValidPath($path)
642
+                and !Filesystem::isFileBlacklisted($path)
643
+            ) {
644
+                $path = $this->getRelativePath($absolutePath);
645
+
646
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
647
+
648
+                $exists = $this->file_exists($path);
649
+                $run = true;
650
+                if ($this->shouldEmitHooks($path)) {
651
+                    $this->emit_file_hooks_pre($exists, $path, $run);
652
+                }
653
+                if (!$run) {
654
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
655
+                    return false;
656
+                }
657
+
658
+                $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
659
+
660
+                /** @var \OC\Files\Storage\Storage $storage */
661
+                list($storage, $internalPath) = $this->resolvePath($path);
662
+                $target = $storage->fopen($internalPath, 'w');
663
+                if ($target) {
664
+                    list (, $result) = \OC_Helper::streamCopy($data, $target);
665
+                    fclose($target);
666
+                    fclose($data);
667
+
668
+                    $this->writeUpdate($storage, $internalPath);
669
+
670
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
671
+
672
+                    if ($this->shouldEmitHooks($path) && $result !== false) {
673
+                        $this->emit_file_hooks_post($exists, $path);
674
+                    }
675
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
676
+                    return $result;
677
+                } else {
678
+                    $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
679
+                    return false;
680
+                }
681
+            } else {
682
+                return false;
683
+            }
684
+        } else {
685
+            $hooks = $this->file_exists($path) ? array('update', 'write') : array('create', 'write');
686
+            return $this->basicOperation('file_put_contents', $path, $hooks, $data);
687
+        }
688
+    }
689
+
690
+    /**
691
+     * @param string $path
692
+     * @return bool|mixed
693
+     */
694
+    public function unlink($path) {
695
+        if ($path === '' || $path === '/') {
696
+            // do not allow deleting the root
697
+            return false;
698
+        }
699
+        $postFix = (substr($path, -1) === '/') ? '/' : '';
700
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
701
+        $mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
702
+        if ($mount and $mount->getInternalPath($absolutePath) === '') {
703
+            return $this->removeMount($mount, $absolutePath);
704
+        }
705
+        if ($this->is_dir($path)) {
706
+            $result = $this->basicOperation('rmdir', $path, ['delete']);
707
+        } else {
708
+            $result = $this->basicOperation('unlink', $path, ['delete']);
709
+        }
710
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
711
+            $storage = $mount->getStorage();
712
+            $internalPath = $mount->getInternalPath($absolutePath);
713
+            $storage->getUpdater()->remove($internalPath);
714
+            return true;
715
+        } else {
716
+            return $result;
717
+        }
718
+    }
719
+
720
+    /**
721
+     * @param string $directory
722
+     * @return bool|mixed
723
+     */
724
+    public function deleteAll($directory) {
725
+        return $this->rmdir($directory);
726
+    }
727
+
728
+    /**
729
+     * Rename/move a file or folder from the source path to target path.
730
+     *
731
+     * @param string $path1 source path
732
+     * @param string $path2 target path
733
+     *
734
+     * @return bool|mixed
735
+     */
736
+    public function rename($path1, $path2) {
737
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
738
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
739
+        $result = false;
740
+        if (
741
+            Filesystem::isValidPath($path2)
742
+            and Filesystem::isValidPath($path1)
743
+            and !Filesystem::isFileBlacklisted($path2)
744
+        ) {
745
+            $path1 = $this->getRelativePath($absolutePath1);
746
+            $path2 = $this->getRelativePath($absolutePath2);
747
+            $exists = $this->file_exists($path2);
748
+
749
+            if ($path1 == null or $path2 == null) {
750
+                return false;
751
+            }
752
+
753
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
754
+            try {
755
+                $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
756
+
757
+                $run = true;
758
+                if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
759
+                    // if it was a rename from a part file to a regular file it was a write and not a rename operation
760
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
761
+                } elseif ($this->shouldEmitHooks($path1)) {
762
+                    \OC_Hook::emit(
763
+                        Filesystem::CLASSNAME, Filesystem::signal_rename,
764
+                        array(
765
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
766
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
767
+                            Filesystem::signal_param_run => &$run
768
+                        )
769
+                    );
770
+                }
771
+                if ($run) {
772
+                    $this->verifyPath(dirname($path2), basename($path2));
773
+
774
+                    $manager = Filesystem::getMountManager();
775
+                    $mount1 = $this->getMount($path1);
776
+                    $mount2 = $this->getMount($path2);
777
+                    $storage1 = $mount1->getStorage();
778
+                    $storage2 = $mount2->getStorage();
779
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
780
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
781
+
782
+                    $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
783
+                    try {
784
+                        $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
785
+
786
+                        if ($internalPath1 === '') {
787
+                            if ($mount1 instanceof MoveableMount) {
788
+                                if ($this->isTargetAllowed($absolutePath2)) {
789
+                                    /**
790
+                                     * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
791
+                                     */
792
+                                    $sourceMountPoint = $mount1->getMountPoint();
793
+                                    $result = $mount1->moveMount($absolutePath2);
794
+                                    $manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
795
+                                } else {
796
+                                    $result = false;
797
+                                }
798
+                            } else {
799
+                                $result = false;
800
+                            }
801
+                            // moving a file/folder within the same mount point
802
+                        } elseif ($storage1 === $storage2) {
803
+                            if ($storage1) {
804
+                                $result = $storage1->rename($internalPath1, $internalPath2);
805
+                            } else {
806
+                                $result = false;
807
+                            }
808
+                            // moving a file/folder between storages (from $storage1 to $storage2)
809
+                        } else {
810
+                            $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
811
+                        }
812
+
813
+                        if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
814
+                            // if it was a rename from a part file to a regular file it was a write and not a rename operation
815
+                            $this->writeUpdate($storage2, $internalPath2);
816
+                        } else if ($result) {
817
+                            if ($internalPath1 !== '') { // don't do a cache update for moved mounts
818
+                                $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
819
+                            }
820
+                        }
821
+                    } catch(\Exception $e) {
822
+                        throw $e;
823
+                    } finally {
824
+                        $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
825
+                        $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
826
+                    }
827
+
828
+                    if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
829
+                        if ($this->shouldEmitHooks()) {
830
+                            $this->emit_file_hooks_post($exists, $path2);
831
+                        }
832
+                    } elseif ($result) {
833
+                        if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
834
+                            \OC_Hook::emit(
835
+                                Filesystem::CLASSNAME,
836
+                                Filesystem::signal_post_rename,
837
+                                array(
838
+                                    Filesystem::signal_param_oldpath => $this->getHookPath($path1),
839
+                                    Filesystem::signal_param_newpath => $this->getHookPath($path2)
840
+                                )
841
+                            );
842
+                        }
843
+                    }
844
+                }
845
+            } catch(\Exception $e) {
846
+                throw $e;
847
+            } finally {
848
+                $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
849
+                $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
850
+            }
851
+        }
852
+        return $result;
853
+    }
854
+
855
+    /**
856
+     * Copy a file/folder from the source path to target path
857
+     *
858
+     * @param string $path1 source path
859
+     * @param string $path2 target path
860
+     * @param bool $preserveMtime whether to preserve mtime on the copy
861
+     *
862
+     * @return bool|mixed
863
+     */
864
+    public function copy($path1, $path2, $preserveMtime = false) {
865
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
866
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
867
+        $result = false;
868
+        if (
869
+            Filesystem::isValidPath($path2)
870
+            and Filesystem::isValidPath($path1)
871
+            and !Filesystem::isFileBlacklisted($path2)
872
+        ) {
873
+            $path1 = $this->getRelativePath($absolutePath1);
874
+            $path2 = $this->getRelativePath($absolutePath2);
875
+
876
+            if ($path1 == null or $path2 == null) {
877
+                return false;
878
+            }
879
+            $run = true;
880
+
881
+            $this->lockFile($path2, ILockingProvider::LOCK_SHARED);
882
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED);
883
+            $lockTypePath1 = ILockingProvider::LOCK_SHARED;
884
+            $lockTypePath2 = ILockingProvider::LOCK_SHARED;
885
+
886
+            try {
887
+
888
+                $exists = $this->file_exists($path2);
889
+                if ($this->shouldEmitHooks()) {
890
+                    \OC_Hook::emit(
891
+                        Filesystem::CLASSNAME,
892
+                        Filesystem::signal_copy,
893
+                        array(
894
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
895
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
896
+                            Filesystem::signal_param_run => &$run
897
+                        )
898
+                    );
899
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
900
+                }
901
+                if ($run) {
902
+                    $mount1 = $this->getMount($path1);
903
+                    $mount2 = $this->getMount($path2);
904
+                    $storage1 = $mount1->getStorage();
905
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
906
+                    $storage2 = $mount2->getStorage();
907
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
908
+
909
+                    $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
910
+                    $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
911
+
912
+                    if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
913
+                        if ($storage1) {
914
+                            $result = $storage1->copy($internalPath1, $internalPath2);
915
+                        } else {
916
+                            $result = false;
917
+                        }
918
+                    } else {
919
+                        $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
920
+                    }
921
+
922
+                    $this->writeUpdate($storage2, $internalPath2);
923
+
924
+                    $this->changeLock($path2, ILockingProvider::LOCK_SHARED);
925
+                    $lockTypePath2 = ILockingProvider::LOCK_SHARED;
926
+
927
+                    if ($this->shouldEmitHooks() && $result !== false) {
928
+                        \OC_Hook::emit(
929
+                            Filesystem::CLASSNAME,
930
+                            Filesystem::signal_post_copy,
931
+                            array(
932
+                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
933
+                                Filesystem::signal_param_newpath => $this->getHookPath($path2)
934
+                            )
935
+                        );
936
+                        $this->emit_file_hooks_post($exists, $path2);
937
+                    }
938
+
939
+                }
940
+            } catch (\Exception $e) {
941
+                $this->unlockFile($path2, $lockTypePath2);
942
+                $this->unlockFile($path1, $lockTypePath1);
943
+                throw $e;
944
+            }
945
+
946
+            $this->unlockFile($path2, $lockTypePath2);
947
+            $this->unlockFile($path1, $lockTypePath1);
948
+
949
+        }
950
+        return $result;
951
+    }
952
+
953
+    /**
954
+     * @param string $path
955
+     * @param string $mode 'r' or 'w'
956
+     * @return resource
957
+     */
958
+    public function fopen($path, $mode) {
959
+        $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
960
+        $hooks = array();
961
+        switch ($mode) {
962
+            case 'r':
963
+                $hooks[] = 'read';
964
+                break;
965
+            case 'r+':
966
+            case 'w+':
967
+            case 'x+':
968
+            case 'a+':
969
+                $hooks[] = 'read';
970
+                $hooks[] = 'write';
971
+                break;
972
+            case 'w':
973
+            case 'x':
974
+            case 'a':
975
+                $hooks[] = 'write';
976
+                break;
977
+            default:
978
+                \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
979
+        }
980
+
981
+        if ($mode !== 'r' && $mode !== 'w') {
982
+            \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');
983
+        }
984
+
985
+        return $this->basicOperation('fopen', $path, $hooks, $mode);
986
+    }
987
+
988
+    /**
989
+     * @param string $path
990
+     * @return bool|string
991
+     * @throws \OCP\Files\InvalidPathException
992
+     */
993
+    public function toTmpFile($path) {
994
+        $this->assertPathLength($path);
995
+        if (Filesystem::isValidPath($path)) {
996
+            $source = $this->fopen($path, 'r');
997
+            if ($source) {
998
+                $extension = pathinfo($path, PATHINFO_EXTENSION);
999
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1000
+                file_put_contents($tmpFile, $source);
1001
+                return $tmpFile;
1002
+            } else {
1003
+                return false;
1004
+            }
1005
+        } else {
1006
+            return false;
1007
+        }
1008
+    }
1009
+
1010
+    /**
1011
+     * @param string $tmpFile
1012
+     * @param string $path
1013
+     * @return bool|mixed
1014
+     * @throws \OCP\Files\InvalidPathException
1015
+     */
1016
+    public function fromTmpFile($tmpFile, $path) {
1017
+        $this->assertPathLength($path);
1018
+        if (Filesystem::isValidPath($path)) {
1019
+
1020
+            // Get directory that the file is going into
1021
+            $filePath = dirname($path);
1022
+
1023
+            // Create the directories if any
1024
+            if (!$this->file_exists($filePath)) {
1025
+                $result = $this->createParentDirectories($filePath);
1026
+                if ($result === false) {
1027
+                    return false;
1028
+                }
1029
+            }
1030
+
1031
+            $source = fopen($tmpFile, 'r');
1032
+            if ($source) {
1033
+                $result = $this->file_put_contents($path, $source);
1034
+                // $this->file_put_contents() might have already closed
1035
+                // the resource, so we check it, before trying to close it
1036
+                // to avoid messages in the error log.
1037
+                if (is_resource($source)) {
1038
+                    fclose($source);
1039
+                }
1040
+                unlink($tmpFile);
1041
+                return $result;
1042
+            } else {
1043
+                return false;
1044
+            }
1045
+        } else {
1046
+            return false;
1047
+        }
1048
+    }
1049
+
1050
+
1051
+    /**
1052
+     * @param string $path
1053
+     * @return mixed
1054
+     * @throws \OCP\Files\InvalidPathException
1055
+     */
1056
+    public function getMimeType($path) {
1057
+        $this->assertPathLength($path);
1058
+        return $this->basicOperation('getMimeType', $path);
1059
+    }
1060
+
1061
+    /**
1062
+     * @param string $type
1063
+     * @param string $path
1064
+     * @param bool $raw
1065
+     * @return bool|null|string
1066
+     */
1067
+    public function hash($type, $path, $raw = false) {
1068
+        $postFix = (substr($path, -1) === '/') ? '/' : '';
1069
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1070
+        if (Filesystem::isValidPath($path)) {
1071
+            $path = $this->getRelativePath($absolutePath);
1072
+            if ($path == null) {
1073
+                return false;
1074
+            }
1075
+            if ($this->shouldEmitHooks($path)) {
1076
+                \OC_Hook::emit(
1077
+                    Filesystem::CLASSNAME,
1078
+                    Filesystem::signal_read,
1079
+                    array(Filesystem::signal_param_path => $this->getHookPath($path))
1080
+                );
1081
+            }
1082
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1083
+            if ($storage) {
1084
+                return $storage->hash($type, $internalPath, $raw);
1085
+            }
1086
+        }
1087
+        return null;
1088
+    }
1089
+
1090
+    /**
1091
+     * @param string $path
1092
+     * @return mixed
1093
+     * @throws \OCP\Files\InvalidPathException
1094
+     */
1095
+    public function free_space($path = '/') {
1096
+        $this->assertPathLength($path);
1097
+        $result = $this->basicOperation('free_space', $path);
1098
+        if ($result === null) {
1099
+            throw new InvalidPathException();
1100
+        }
1101
+        return $result;
1102
+    }
1103
+
1104
+    /**
1105
+     * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1106
+     *
1107
+     * @param string $operation
1108
+     * @param string $path
1109
+     * @param array $hooks (optional)
1110
+     * @param mixed $extraParam (optional)
1111
+     * @return mixed
1112
+     * @throws \Exception
1113
+     *
1114
+     * This method takes requests for basic filesystem functions (e.g. reading & writing
1115
+     * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1116
+     * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1117
+     */
1118
+    private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1119
+        $postFix = (substr($path, -1) === '/') ? '/' : '';
1120
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1121
+        if (Filesystem::isValidPath($path)
1122
+            and !Filesystem::isFileBlacklisted($path)
1123
+        ) {
1124
+            $path = $this->getRelativePath($absolutePath);
1125
+            if ($path == null) {
1126
+                return false;
1127
+            }
1128
+
1129
+            if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1130
+                // always a shared lock during pre-hooks so the hook can read the file
1131
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
1132
+            }
1133
+
1134
+            $run = $this->runHooks($hooks, $path);
1135
+            /** @var \OC\Files\Storage\Storage $storage */
1136
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1137
+            if ($run and $storage) {
1138
+                if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1139
+                    $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1140
+                }
1141
+                try {
1142
+                    if (!is_null($extraParam)) {
1143
+                        $result = $storage->$operation($internalPath, $extraParam);
1144
+                    } else {
1145
+                        $result = $storage->$operation($internalPath);
1146
+                    }
1147
+                } catch (\Exception $e) {
1148
+                    if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1149
+                        $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1150
+                    } else if (in_array('read', $hooks)) {
1151
+                        $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1152
+                    }
1153
+                    throw $e;
1154
+                }
1155
+
1156
+                if ($result && in_array('delete', $hooks) and $result) {
1157
+                    $this->removeUpdate($storage, $internalPath);
1158
+                }
1159
+                if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1160
+                    $this->writeUpdate($storage, $internalPath);
1161
+                }
1162
+                if ($result && in_array('touch', $hooks)) {
1163
+                    $this->writeUpdate($storage, $internalPath, $extraParam);
1164
+                }
1165
+
1166
+                if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1167
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
1168
+                }
1169
+
1170
+                $unlockLater = false;
1171
+                if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1172
+                    $unlockLater = true;
1173
+                    // make sure our unlocking callback will still be called if connection is aborted
1174
+                    ignore_user_abort(true);
1175
+                    $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1176
+                        if (in_array('write', $hooks)) {
1177
+                            $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1178
+                        } else if (in_array('read', $hooks)) {
1179
+                            $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1180
+                        }
1181
+                    });
1182
+                }
1183
+
1184
+                if ($this->shouldEmitHooks($path) && $result !== false) {
1185
+                    if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1186
+                        $this->runHooks($hooks, $path, true);
1187
+                    }
1188
+                }
1189
+
1190
+                if (!$unlockLater
1191
+                    && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1192
+                ) {
1193
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1194
+                }
1195
+                return $result;
1196
+            } else {
1197
+                $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1198
+            }
1199
+        }
1200
+        return null;
1201
+    }
1202
+
1203
+    /**
1204
+     * get the path relative to the default root for hook usage
1205
+     *
1206
+     * @param string $path
1207
+     * @return string
1208
+     */
1209
+    private function getHookPath($path) {
1210
+        if (!Filesystem::getView()) {
1211
+            return $path;
1212
+        }
1213
+        return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1214
+    }
1215
+
1216
+    private function shouldEmitHooks($path = '') {
1217
+        if ($path && Cache\Scanner::isPartialFile($path)) {
1218
+            return false;
1219
+        }
1220
+        if (!Filesystem::$loaded) {
1221
+            return false;
1222
+        }
1223
+        $defaultRoot = Filesystem::getRoot();
1224
+        if ($defaultRoot === null) {
1225
+            return false;
1226
+        }
1227
+        if ($this->fakeRoot === $defaultRoot) {
1228
+            return true;
1229
+        }
1230
+        $fullPath = $this->getAbsolutePath($path);
1231
+
1232
+        if ($fullPath === $defaultRoot) {
1233
+            return true;
1234
+        }
1235
+
1236
+        return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1237
+    }
1238
+
1239
+    /**
1240
+     * @param string[] $hooks
1241
+     * @param string $path
1242
+     * @param bool $post
1243
+     * @return bool
1244
+     */
1245
+    private function runHooks($hooks, $path, $post = false) {
1246
+        $relativePath = $path;
1247
+        $path = $this->getHookPath($path);
1248
+        $prefix = $post ? 'post_' : '';
1249
+        $run = true;
1250
+        if ($this->shouldEmitHooks($relativePath)) {
1251
+            foreach ($hooks as $hook) {
1252
+                if ($hook != 'read') {
1253
+                    \OC_Hook::emit(
1254
+                        Filesystem::CLASSNAME,
1255
+                        $prefix . $hook,
1256
+                        array(
1257
+                            Filesystem::signal_param_run => &$run,
1258
+                            Filesystem::signal_param_path => $path
1259
+                        )
1260
+                    );
1261
+                } elseif (!$post) {
1262
+                    \OC_Hook::emit(
1263
+                        Filesystem::CLASSNAME,
1264
+                        $prefix . $hook,
1265
+                        array(
1266
+                            Filesystem::signal_param_path => $path
1267
+                        )
1268
+                    );
1269
+                }
1270
+            }
1271
+        }
1272
+        return $run;
1273
+    }
1274
+
1275
+    /**
1276
+     * check if a file or folder has been updated since $time
1277
+     *
1278
+     * @param string $path
1279
+     * @param int $time
1280
+     * @return bool
1281
+     */
1282
+    public function hasUpdated($path, $time) {
1283
+        return $this->basicOperation('hasUpdated', $path, array(), $time);
1284
+    }
1285
+
1286
+    /**
1287
+     * @param string $ownerId
1288
+     * @return \OC\User\User
1289
+     */
1290
+    private function getUserObjectForOwner($ownerId) {
1291
+        $owner = $this->userManager->get($ownerId);
1292
+        if ($owner instanceof IUser) {
1293
+            return $owner;
1294
+        } else {
1295
+            return new User($ownerId, null);
1296
+        }
1297
+    }
1298
+
1299
+    /**
1300
+     * Get file info from cache
1301
+     *
1302
+     * If the file is not in cached it will be scanned
1303
+     * If the file has changed on storage the cache will be updated
1304
+     *
1305
+     * @param \OC\Files\Storage\Storage $storage
1306
+     * @param string $internalPath
1307
+     * @param string $relativePath
1308
+     * @return ICacheEntry|bool
1309
+     */
1310
+    private function getCacheEntry($storage, $internalPath, $relativePath) {
1311
+        $cache = $storage->getCache($internalPath);
1312
+        $data = $cache->get($internalPath);
1313
+        $watcher = $storage->getWatcher($internalPath);
1314
+
1315
+        try {
1316
+            // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1317
+            if (!$data || $data['size'] === -1) {
1318
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1319
+                if (!$storage->file_exists($internalPath)) {
1320
+                    $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1321
+                    return false;
1322
+                }
1323
+                $scanner = $storage->getScanner($internalPath);
1324
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1325
+                $data = $cache->get($internalPath);
1326
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1327
+            } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1328
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1329
+                $watcher->update($internalPath, $data);
1330
+                $storage->getPropagator()->propagateChange($internalPath, time());
1331
+                $data = $cache->get($internalPath);
1332
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1333
+            }
1334
+        } catch (LockedException $e) {
1335
+            // if the file is locked we just use the old cache info
1336
+        }
1337
+
1338
+        return $data;
1339
+    }
1340
+
1341
+    /**
1342
+     * get the filesystem info
1343
+     *
1344
+     * @param string $path
1345
+     * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1346
+     * 'ext' to add only ext storage mount point sizes. Defaults to true.
1347
+     * defaults to true
1348
+     * @return \OC\Files\FileInfo|false False if file does not exist
1349
+     */
1350
+    public function getFileInfo($path, $includeMountPoints = true) {
1351
+        $this->assertPathLength($path);
1352
+        if (!Filesystem::isValidPath($path)) {
1353
+            return false;
1354
+        }
1355
+        if (Cache\Scanner::isPartialFile($path)) {
1356
+            return $this->getPartFileInfo($path);
1357
+        }
1358
+        $relativePath = $path;
1359
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1360
+
1361
+        $mount = Filesystem::getMountManager()->find($path);
1362
+        if (!$mount) {
1363
+            return false;
1364
+        }
1365
+        $storage = $mount->getStorage();
1366
+        $internalPath = $mount->getInternalPath($path);
1367
+        if ($storage) {
1368
+            $data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1369
+
1370
+            if (!$data instanceof ICacheEntry) {
1371
+                return false;
1372
+            }
1373
+
1374
+            if ($mount instanceof MoveableMount && $internalPath === '') {
1375
+                $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1376
+            }
1377
+
1378
+            $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1379
+            $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1380
+
1381
+            if ($data and isset($data['fileid'])) {
1382
+                if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1383
+                    //add the sizes of other mount points to the folder
1384
+                    $extOnly = ($includeMountPoints === 'ext');
1385
+                    $mounts = Filesystem::getMountManager()->findIn($path);
1386
+                    $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1387
+                        $subStorage = $mount->getStorage();
1388
+                        return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1389
+                    }));
1390
+                }
1391
+            }
1392
+
1393
+            return $info;
1394
+        }
1395
+
1396
+        return false;
1397
+    }
1398
+
1399
+    /**
1400
+     * get the content of a directory
1401
+     *
1402
+     * @param string $directory path under datadirectory
1403
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1404
+     * @return FileInfo[]
1405
+     */
1406
+    public function getDirectoryContent($directory, $mimetype_filter = '') {
1407
+        $this->assertPathLength($directory);
1408
+        if (!Filesystem::isValidPath($directory)) {
1409
+            return [];
1410
+        }
1411
+        $path = $this->getAbsolutePath($directory);
1412
+        $path = Filesystem::normalizePath($path);
1413
+        $mount = $this->getMount($directory);
1414
+        if (!$mount) {
1415
+            return [];
1416
+        }
1417
+        $storage = $mount->getStorage();
1418
+        $internalPath = $mount->getInternalPath($path);
1419
+        if ($storage) {
1420
+            $cache = $storage->getCache($internalPath);
1421
+            $user = \OC_User::getUser();
1422
+
1423
+            $data = $this->getCacheEntry($storage, $internalPath, $directory);
1424
+
1425
+            if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1426
+                return [];
1427
+            }
1428
+
1429
+            $folderId = $data['fileid'];
1430
+            $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1431
+
1432
+            $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1433
+            /**
1434
+             * @var \OC\Files\FileInfo[] $files
1435
+             */
1436
+            $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1437
+                if ($sharingDisabled) {
1438
+                    $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1439
+                }
1440
+                $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1441
+                return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1442
+            }, $contents);
1443
+
1444
+            //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1445
+            $mounts = Filesystem::getMountManager()->findIn($path);
1446
+            $dirLength = strlen($path);
1447
+            foreach ($mounts as $mount) {
1448
+                $mountPoint = $mount->getMountPoint();
1449
+                $subStorage = $mount->getStorage();
1450
+                if ($subStorage) {
1451
+                    $subCache = $subStorage->getCache('');
1452
+
1453
+                    $rootEntry = $subCache->get('');
1454
+                    if (!$rootEntry) {
1455
+                        $subScanner = $subStorage->getScanner('');
1456
+                        try {
1457
+                            $subScanner->scanFile('');
1458
+                        } catch (\OCP\Files\StorageNotAvailableException $e) {
1459
+                            continue;
1460
+                        } catch (\OCP\Files\StorageInvalidException $e) {
1461
+                            continue;
1462
+                        } catch (\Exception $e) {
1463
+                            // sometimes when the storage is not available it can be any exception
1464
+                            \OC::$server->getLogger()->logException($e, [
1465
+                                'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1466
+                                'level' => \OCP\Util::ERROR,
1467
+                                'app' => 'lib',
1468
+                            ]);
1469
+                            continue;
1470
+                        }
1471
+                        $rootEntry = $subCache->get('');
1472
+                    }
1473
+
1474
+                    if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1475
+                        $relativePath = trim(substr($mountPoint, $dirLength), '/');
1476
+                        if ($pos = strpos($relativePath, '/')) {
1477
+                            //mountpoint inside subfolder add size to the correct folder
1478
+                            $entryName = substr($relativePath, 0, $pos);
1479
+                            foreach ($files as &$entry) {
1480
+                                if ($entry->getName() === $entryName) {
1481
+                                    $entry->addSubEntry($rootEntry, $mountPoint);
1482
+                                }
1483
+                            }
1484
+                        } else { //mountpoint in this folder, add an entry for it
1485
+                            $rootEntry['name'] = $relativePath;
1486
+                            $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1487
+                            $permissions = $rootEntry['permissions'];
1488
+                            // do not allow renaming/deleting the mount point if they are not shared files/folders
1489
+                            // for shared files/folders we use the permissions given by the owner
1490
+                            if ($mount instanceof MoveableMount) {
1491
+                                $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1492
+                            } else {
1493
+                                $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1494
+                            }
1495
+
1496
+                            //remove any existing entry with the same name
1497
+                            foreach ($files as $i => $file) {
1498
+                                if ($file['name'] === $rootEntry['name']) {
1499
+                                    unset($files[$i]);
1500
+                                    break;
1501
+                                }
1502
+                            }
1503
+                            $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1504
+
1505
+                            // if sharing was disabled for the user we remove the share permissions
1506
+                            if (\OCP\Util::isSharingDisabledForUser()) {
1507
+                                $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1508
+                            }
1509
+
1510
+                            $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1511
+                            $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1512
+                        }
1513
+                    }
1514
+                }
1515
+            }
1516
+
1517
+            if ($mimetype_filter) {
1518
+                $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1519
+                    if (strpos($mimetype_filter, '/')) {
1520
+                        return $file->getMimetype() === $mimetype_filter;
1521
+                    } else {
1522
+                        return $file->getMimePart() === $mimetype_filter;
1523
+                    }
1524
+                });
1525
+            }
1526
+
1527
+            return $files;
1528
+        } else {
1529
+            return [];
1530
+        }
1531
+    }
1532
+
1533
+    /**
1534
+     * change file metadata
1535
+     *
1536
+     * @param string $path
1537
+     * @param array|\OCP\Files\FileInfo $data
1538
+     * @return int
1539
+     *
1540
+     * returns the fileid of the updated file
1541
+     */
1542
+    public function putFileInfo($path, $data) {
1543
+        $this->assertPathLength($path);
1544
+        if ($data instanceof FileInfo) {
1545
+            $data = $data->getData();
1546
+        }
1547
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1548
+        /**
1549
+         * @var \OC\Files\Storage\Storage $storage
1550
+         * @var string $internalPath
1551
+         */
1552
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
1553
+        if ($storage) {
1554
+            $cache = $storage->getCache($path);
1555
+
1556
+            if (!$cache->inCache($internalPath)) {
1557
+                $scanner = $storage->getScanner($internalPath);
1558
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1559
+            }
1560
+
1561
+            return $cache->put($internalPath, $data);
1562
+        } else {
1563
+            return -1;
1564
+        }
1565
+    }
1566
+
1567
+    /**
1568
+     * search for files with the name matching $query
1569
+     *
1570
+     * @param string $query
1571
+     * @return FileInfo[]
1572
+     */
1573
+    public function search($query) {
1574
+        return $this->searchCommon('search', array('%' . $query . '%'));
1575
+    }
1576
+
1577
+    /**
1578
+     * search for files with the name matching $query
1579
+     *
1580
+     * @param string $query
1581
+     * @return FileInfo[]
1582
+     */
1583
+    public function searchRaw($query) {
1584
+        return $this->searchCommon('search', array($query));
1585
+    }
1586
+
1587
+    /**
1588
+     * search for files by mimetype
1589
+     *
1590
+     * @param string $mimetype
1591
+     * @return FileInfo[]
1592
+     */
1593
+    public function searchByMime($mimetype) {
1594
+        return $this->searchCommon('searchByMime', array($mimetype));
1595
+    }
1596
+
1597
+    /**
1598
+     * search for files by tag
1599
+     *
1600
+     * @param string|int $tag name or tag id
1601
+     * @param string $userId owner of the tags
1602
+     * @return FileInfo[]
1603
+     */
1604
+    public function searchByTag($tag, $userId) {
1605
+        return $this->searchCommon('searchByTag', array($tag, $userId));
1606
+    }
1607
+
1608
+    /**
1609
+     * @param string $method cache method
1610
+     * @param array $args
1611
+     * @return FileInfo[]
1612
+     */
1613
+    private function searchCommon($method, $args) {
1614
+        $files = array();
1615
+        $rootLength = strlen($this->fakeRoot);
1616
+
1617
+        $mount = $this->getMount('');
1618
+        $mountPoint = $mount->getMountPoint();
1619
+        $storage = $mount->getStorage();
1620
+        if ($storage) {
1621
+            $cache = $storage->getCache('');
1622
+
1623
+            $results = call_user_func_array(array($cache, $method), $args);
1624
+            foreach ($results as $result) {
1625
+                if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1626
+                    $internalPath = $result['path'];
1627
+                    $path = $mountPoint . $result['path'];
1628
+                    $result['path'] = substr($mountPoint . $result['path'], $rootLength);
1629
+                    $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1630
+                    $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1631
+                }
1632
+            }
1633
+
1634
+            $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1635
+            foreach ($mounts as $mount) {
1636
+                $mountPoint = $mount->getMountPoint();
1637
+                $storage = $mount->getStorage();
1638
+                if ($storage) {
1639
+                    $cache = $storage->getCache('');
1640
+
1641
+                    $relativeMountPoint = substr($mountPoint, $rootLength);
1642
+                    $results = call_user_func_array(array($cache, $method), $args);
1643
+                    if ($results) {
1644
+                        foreach ($results as $result) {
1645
+                            $internalPath = $result['path'];
1646
+                            $result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1647
+                            $path = rtrim($mountPoint . $internalPath, '/');
1648
+                            $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1649
+                            $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1650
+                        }
1651
+                    }
1652
+                }
1653
+            }
1654
+        }
1655
+        return $files;
1656
+    }
1657
+
1658
+    /**
1659
+     * Get the owner for a file or folder
1660
+     *
1661
+     * @param string $path
1662
+     * @return string the user id of the owner
1663
+     * @throws NotFoundException
1664
+     */
1665
+    public function getOwner($path) {
1666
+        $info = $this->getFileInfo($path);
1667
+        if (!$info) {
1668
+            throw new NotFoundException($path . ' not found while trying to get owner');
1669
+        }
1670
+        return $info->getOwner()->getUID();
1671
+    }
1672
+
1673
+    /**
1674
+     * get the ETag for a file or folder
1675
+     *
1676
+     * @param string $path
1677
+     * @return string
1678
+     */
1679
+    public function getETag($path) {
1680
+        /**
1681
+         * @var Storage\Storage $storage
1682
+         * @var string $internalPath
1683
+         */
1684
+        list($storage, $internalPath) = $this->resolvePath($path);
1685
+        if ($storage) {
1686
+            return $storage->getETag($internalPath);
1687
+        } else {
1688
+            return null;
1689
+        }
1690
+    }
1691
+
1692
+    /**
1693
+     * Get the path of a file by id, relative to the view
1694
+     *
1695
+     * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1696
+     *
1697
+     * @param int $id
1698
+     * @throws NotFoundException
1699
+     * @return string
1700
+     */
1701
+    public function getPath($id) {
1702
+        $id = (int)$id;
1703
+        $manager = Filesystem::getMountManager();
1704
+        $mounts = $manager->findIn($this->fakeRoot);
1705
+        $mounts[] = $manager->find($this->fakeRoot);
1706
+        // reverse the array so we start with the storage this view is in
1707
+        // which is the most likely to contain the file we're looking for
1708
+        $mounts = array_reverse($mounts);
1709
+        foreach ($mounts as $mount) {
1710
+            /**
1711
+             * @var \OC\Files\Mount\MountPoint $mount
1712
+             */
1713
+            if ($mount->getStorage()) {
1714
+                $cache = $mount->getStorage()->getCache();
1715
+                $internalPath = $cache->getPathById($id);
1716
+                if (is_string($internalPath)) {
1717
+                    $fullPath = $mount->getMountPoint() . $internalPath;
1718
+                    if (!is_null($path = $this->getRelativePath($fullPath))) {
1719
+                        return $path;
1720
+                    }
1721
+                }
1722
+            }
1723
+        }
1724
+        throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1725
+    }
1726
+
1727
+    /**
1728
+     * @param string $path
1729
+     * @throws InvalidPathException
1730
+     */
1731
+    private function assertPathLength($path) {
1732
+        $maxLen = min(PHP_MAXPATHLEN, 4000);
1733
+        // Check for the string length - performed using isset() instead of strlen()
1734
+        // because isset() is about 5x-40x faster.
1735
+        if (isset($path[$maxLen])) {
1736
+            $pathLen = strlen($path);
1737
+            throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1738
+        }
1739
+    }
1740
+
1741
+    /**
1742
+     * check if it is allowed to move a mount point to a given target.
1743
+     * It is not allowed to move a mount point into a different mount point or
1744
+     * into an already shared folder
1745
+     *
1746
+     * @param string $target path
1747
+     * @return boolean
1748
+     */
1749
+    private function isTargetAllowed($target) {
1750
+
1751
+        list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1752
+        if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1753
+            \OCP\Util::writeLog('files',
1754
+                'It is not allowed to move one mount point into another one',
1755
+                \OCP\Util::DEBUG);
1756
+            return false;
1757
+        }
1758
+
1759
+        // note: cannot use the view because the target is already locked
1760
+        $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1761
+        if ($fileId === -1) {
1762
+            // target might not exist, need to check parent instead
1763
+            $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1764
+        }
1765
+
1766
+        // check if any of the parents were shared by the current owner (include collections)
1767
+        $shares = \OCP\Share::getItemShared(
1768
+            'folder',
1769
+            $fileId,
1770
+            \OCP\Share::FORMAT_NONE,
1771
+            null,
1772
+            true
1773
+        );
1774
+
1775
+        if (count($shares) > 0) {
1776
+            \OCP\Util::writeLog('files',
1777
+                'It is not allowed to move one mount point into a shared folder',
1778
+                \OCP\Util::DEBUG);
1779
+            return false;
1780
+        }
1781
+
1782
+        return true;
1783
+    }
1784
+
1785
+    /**
1786
+     * Get a fileinfo object for files that are ignored in the cache (part files)
1787
+     *
1788
+     * @param string $path
1789
+     * @return \OCP\Files\FileInfo
1790
+     */
1791
+    private function getPartFileInfo($path) {
1792
+        $mount = $this->getMount($path);
1793
+        $storage = $mount->getStorage();
1794
+        $internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1795
+        $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1796
+        return new FileInfo(
1797
+            $this->getAbsolutePath($path),
1798
+            $storage,
1799
+            $internalPath,
1800
+            [
1801
+                'fileid' => null,
1802
+                'mimetype' => $storage->getMimeType($internalPath),
1803
+                'name' => basename($path),
1804
+                'etag' => null,
1805
+                'size' => $storage->filesize($internalPath),
1806
+                'mtime' => $storage->filemtime($internalPath),
1807
+                'encrypted' => false,
1808
+                'permissions' => \OCP\Constants::PERMISSION_ALL
1809
+            ],
1810
+            $mount,
1811
+            $owner
1812
+        );
1813
+    }
1814
+
1815
+    /**
1816
+     * @param string $path
1817
+     * @param string $fileName
1818
+     * @throws InvalidPathException
1819
+     */
1820
+    public function verifyPath($path, $fileName) {
1821
+        try {
1822
+            /** @type \OCP\Files\Storage $storage */
1823
+            list($storage, $internalPath) = $this->resolvePath($path);
1824
+            $storage->verifyPath($internalPath, $fileName);
1825
+        } catch (ReservedWordException $ex) {
1826
+            $l = \OC::$server->getL10N('lib');
1827
+            throw new InvalidPathException($l->t('File name is a reserved word'));
1828
+        } catch (InvalidCharacterInPathException $ex) {
1829
+            $l = \OC::$server->getL10N('lib');
1830
+            throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1831
+        } catch (FileNameTooLongException $ex) {
1832
+            $l = \OC::$server->getL10N('lib');
1833
+            throw new InvalidPathException($l->t('File name is too long'));
1834
+        } catch (InvalidDirectoryException $ex) {
1835
+            $l = \OC::$server->getL10N('lib');
1836
+            throw new InvalidPathException($l->t('Dot files are not allowed'));
1837
+        } catch (EmptyFileNameException $ex) {
1838
+            $l = \OC::$server->getL10N('lib');
1839
+            throw new InvalidPathException($l->t('Empty filename is not allowed'));
1840
+        }
1841
+    }
1842
+
1843
+    /**
1844
+     * get all parent folders of $path
1845
+     *
1846
+     * @param string $path
1847
+     * @return string[]
1848
+     */
1849
+    private function getParents($path) {
1850
+        $path = trim($path, '/');
1851
+        if (!$path) {
1852
+            return [];
1853
+        }
1854
+
1855
+        $parts = explode('/', $path);
1856
+
1857
+        // remove the single file
1858
+        array_pop($parts);
1859
+        $result = array('/');
1860
+        $resultPath = '';
1861
+        foreach ($parts as $part) {
1862
+            if ($part) {
1863
+                $resultPath .= '/' . $part;
1864
+                $result[] = $resultPath;
1865
+            }
1866
+        }
1867
+        return $result;
1868
+    }
1869
+
1870
+    /**
1871
+     * Returns the mount point for which to lock
1872
+     *
1873
+     * @param string $absolutePath absolute path
1874
+     * @param bool $useParentMount true to return parent mount instead of whatever
1875
+     * is mounted directly on the given path, false otherwise
1876
+     * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1877
+     */
1878
+    private function getMountForLock($absolutePath, $useParentMount = false) {
1879
+        $results = [];
1880
+        $mount = Filesystem::getMountManager()->find($absolutePath);
1881
+        if (!$mount) {
1882
+            return $results;
1883
+        }
1884
+
1885
+        if ($useParentMount) {
1886
+            // find out if something is mounted directly on the path
1887
+            $internalPath = $mount->getInternalPath($absolutePath);
1888
+            if ($internalPath === '') {
1889
+                // resolve the parent mount instead
1890
+                $mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1891
+            }
1892
+        }
1893
+
1894
+        return $mount;
1895
+    }
1896
+
1897
+    /**
1898
+     * Lock the given path
1899
+     *
1900
+     * @param string $path the path of the file to lock, relative to the view
1901
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1902
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1903
+     *
1904
+     * @return bool False if the path is excluded from locking, true otherwise
1905
+     * @throws \OCP\Lock\LockedException if the path is already locked
1906
+     */
1907
+    private function lockPath($path, $type, $lockMountPoint = false) {
1908
+        $absolutePath = $this->getAbsolutePath($path);
1909
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1910
+        if (!$this->shouldLockFile($absolutePath)) {
1911
+            return false;
1912
+        }
1913
+
1914
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1915
+        if ($mount) {
1916
+            try {
1917
+                $storage = $mount->getStorage();
1918
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1919
+                    $storage->acquireLock(
1920
+                        $mount->getInternalPath($absolutePath),
1921
+                        $type,
1922
+                        $this->lockingProvider
1923
+                    );
1924
+                }
1925
+            } catch (\OCP\Lock\LockedException $e) {
1926
+                // rethrow with the a human-readable path
1927
+                throw new \OCP\Lock\LockedException(
1928
+                    $this->getPathRelativeToFiles($absolutePath),
1929
+                    $e
1930
+                );
1931
+            }
1932
+        }
1933
+
1934
+        return true;
1935
+    }
1936
+
1937
+    /**
1938
+     * Change the lock type
1939
+     *
1940
+     * @param string $path the path of the file to lock, relative to the view
1941
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1942
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1943
+     *
1944
+     * @return bool False if the path is excluded from locking, true otherwise
1945
+     * @throws \OCP\Lock\LockedException if the path is already locked
1946
+     */
1947
+    public function changeLock($path, $type, $lockMountPoint = false) {
1948
+        $path = Filesystem::normalizePath($path);
1949
+        $absolutePath = $this->getAbsolutePath($path);
1950
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1951
+        if (!$this->shouldLockFile($absolutePath)) {
1952
+            return false;
1953
+        }
1954
+
1955
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1956
+        if ($mount) {
1957
+            try {
1958
+                $storage = $mount->getStorage();
1959
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1960
+                    $storage->changeLock(
1961
+                        $mount->getInternalPath($absolutePath),
1962
+                        $type,
1963
+                        $this->lockingProvider
1964
+                    );
1965
+                }
1966
+            } catch (\OCP\Lock\LockedException $e) {
1967
+                try {
1968
+                    // rethrow with the a human-readable path
1969
+                    throw new \OCP\Lock\LockedException(
1970
+                        $this->getPathRelativeToFiles($absolutePath),
1971
+                        $e
1972
+                    );
1973
+                } catch (\InvalidArgumentException $e) {
1974
+                    throw new \OCP\Lock\LockedException(
1975
+                        $absolutePath,
1976
+                        $e
1977
+                    );
1978
+                }
1979
+            }
1980
+        }
1981
+
1982
+        return true;
1983
+    }
1984
+
1985
+    /**
1986
+     * Unlock the given path
1987
+     *
1988
+     * @param string $path the path of the file to unlock, relative to the view
1989
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1990
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1991
+     *
1992
+     * @return bool False if the path is excluded from locking, true otherwise
1993
+     */
1994
+    private function unlockPath($path, $type, $lockMountPoint = false) {
1995
+        $absolutePath = $this->getAbsolutePath($path);
1996
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1997
+        if (!$this->shouldLockFile($absolutePath)) {
1998
+            return false;
1999
+        }
2000
+
2001
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2002
+        if ($mount) {
2003
+            $storage = $mount->getStorage();
2004
+            if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2005
+                $storage->releaseLock(
2006
+                    $mount->getInternalPath($absolutePath),
2007
+                    $type,
2008
+                    $this->lockingProvider
2009
+                );
2010
+            }
2011
+        }
2012
+
2013
+        return true;
2014
+    }
2015
+
2016
+    /**
2017
+     * Lock a path and all its parents up to the root of the view
2018
+     *
2019
+     * @param string $path the path of the file to lock relative to the view
2020
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2021
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2022
+     *
2023
+     * @return bool False if the path is excluded from locking, true otherwise
2024
+     */
2025
+    public function lockFile($path, $type, $lockMountPoint = false) {
2026
+        $absolutePath = $this->getAbsolutePath($path);
2027
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2028
+        if (!$this->shouldLockFile($absolutePath)) {
2029
+            return false;
2030
+        }
2031
+
2032
+        $this->lockPath($path, $type, $lockMountPoint);
2033
+
2034
+        $parents = $this->getParents($path);
2035
+        foreach ($parents as $parent) {
2036
+            $this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2037
+        }
2038
+
2039
+        return true;
2040
+    }
2041
+
2042
+    /**
2043
+     * Unlock a path and all its parents up to the root of the view
2044
+     *
2045
+     * @param string $path the path of the file to lock relative to the view
2046
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2047
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2048
+     *
2049
+     * @return bool False if the path is excluded from locking, true otherwise
2050
+     */
2051
+    public function unlockFile($path, $type, $lockMountPoint = false) {
2052
+        $absolutePath = $this->getAbsolutePath($path);
2053
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2054
+        if (!$this->shouldLockFile($absolutePath)) {
2055
+            return false;
2056
+        }
2057
+
2058
+        $this->unlockPath($path, $type, $lockMountPoint);
2059
+
2060
+        $parents = $this->getParents($path);
2061
+        foreach ($parents as $parent) {
2062
+            $this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2063
+        }
2064
+
2065
+        return true;
2066
+    }
2067
+
2068
+    /**
2069
+     * Only lock files in data/user/files/
2070
+     *
2071
+     * @param string $path Absolute path to the file/folder we try to (un)lock
2072
+     * @return bool
2073
+     */
2074
+    protected function shouldLockFile($path) {
2075
+        $path = Filesystem::normalizePath($path);
2076
+
2077
+        $pathSegments = explode('/', $path);
2078
+        if (isset($pathSegments[2])) {
2079
+            // E.g.: /username/files/path-to-file
2080
+            return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2081
+        }
2082
+
2083
+        return strpos($path, '/appdata_') !== 0;
2084
+    }
2085
+
2086
+    /**
2087
+     * Shortens the given absolute path to be relative to
2088
+     * "$user/files".
2089
+     *
2090
+     * @param string $absolutePath absolute path which is under "files"
2091
+     *
2092
+     * @return string path relative to "files" with trimmed slashes or null
2093
+     * if the path was NOT relative to files
2094
+     *
2095
+     * @throws \InvalidArgumentException if the given path was not under "files"
2096
+     * @since 8.1.0
2097
+     */
2098
+    public function getPathRelativeToFiles($absolutePath) {
2099
+        $path = Filesystem::normalizePath($absolutePath);
2100
+        $parts = explode('/', trim($path, '/'), 3);
2101
+        // "$user", "files", "path/to/dir"
2102
+        if (!isset($parts[1]) || $parts[1] !== 'files') {
2103
+            $this->logger->error(
2104
+                '$absolutePath must be relative to "files", value is "%s"',
2105
+                [
2106
+                    $absolutePath
2107
+                ]
2108
+            );
2109
+            throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2110
+        }
2111
+        if (isset($parts[2])) {
2112
+            return $parts[2];
2113
+        }
2114
+        return '';
2115
+    }
2116
+
2117
+    /**
2118
+     * @param string $filename
2119
+     * @return array
2120
+     * @throws \OC\User\NoUserException
2121
+     * @throws NotFoundException
2122
+     */
2123
+    public function getUidAndFilename($filename) {
2124
+        $info = $this->getFileInfo($filename);
2125
+        if (!$info instanceof \OCP\Files\FileInfo) {
2126
+            throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2127
+        }
2128
+        $uid = $info->getOwner()->getUID();
2129
+        if ($uid != \OCP\User::getUser()) {
2130
+            Filesystem::initMountPoints($uid);
2131
+            $ownerView = new View('/' . $uid . '/files');
2132
+            try {
2133
+                $filename = $ownerView->getPath($info['fileid']);
2134
+            } catch (NotFoundException $e) {
2135
+                throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2136
+            }
2137
+        }
2138
+        return [$uid, $filename];
2139
+    }
2140
+
2141
+    /**
2142
+     * Creates parent non-existing folders
2143
+     *
2144
+     * @param string $filePath
2145
+     * @return bool
2146
+     */
2147
+    private function createParentDirectories($filePath) {
2148
+        $directoryParts = explode('/', $filePath);
2149
+        $directoryParts = array_filter($directoryParts);
2150
+        foreach ($directoryParts as $key => $part) {
2151
+            $currentPathElements = array_slice($directoryParts, 0, $key);
2152
+            $currentPath = '/' . implode('/', $currentPathElements);
2153
+            if ($this->is_file($currentPath)) {
2154
+                return false;
2155
+            }
2156
+            if (!$this->file_exists($currentPath)) {
2157
+                $this->mkdir($currentPath);
2158
+            }
2159
+        }
2160
+
2161
+        return true;
2162
+    }
2163 2163
 }
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.
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.
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.
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
@@ -49,282 +49,282 @@  discard block
 block discarded – undo
49 49
  * @package OC\Settings\Controller
50 50
  */
51 51
 class CheckSetupController extends Controller {
52
-	/** @var IConfig */
53
-	private $config;
54
-	/** @var IClientService */
55
-	private $clientService;
56
-	/** @var \OC_Util */
57
-	private $util;
58
-	/** @var IURLGenerator */
59
-	private $urlGenerator;
60
-	/** @var IL10N */
61
-	private $l10n;
62
-	/** @var Checker */
63
-	private $checker;
64
-	/** @var ILogger */
65
-	private $logger;
66
-
67
-	/**
68
-	 * @param string $AppName
69
-	 * @param IRequest $request
70
-	 * @param IConfig $config
71
-	 * @param IClientService $clientService
72
-	 * @param IURLGenerator $urlGenerator
73
-	 * @param \OC_Util $util
74
-	 * @param IL10N $l10n
75
-	 * @param Checker $checker
76
-	 * @param ILogger $logger
77
-	 */
78
-	public function __construct($AppName,
79
-								IRequest $request,
80
-								IConfig $config,
81
-								IClientService $clientService,
82
-								IURLGenerator $urlGenerator,
83
-								\OC_Util $util,
84
-								IL10N $l10n,
85
-								Checker $checker,
86
-								ILogger $logger) {
87
-		parent::__construct($AppName, $request);
88
-		$this->config = $config;
89
-		$this->clientService = $clientService;
90
-		$this->util = $util;
91
-		$this->urlGenerator = $urlGenerator;
92
-		$this->l10n = $l10n;
93
-		$this->checker = $checker;
94
-		$this->logger = $logger;
95
-	}
96
-
97
-	/**
98
-	 * Checks if the server can connect to the internet using HTTPS and HTTP
99
-	 * @return bool
100
-	 */
101
-	private function isInternetConnectionWorking() {
102
-		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
103
-			return false;
104
-		}
105
-
106
-		$siteArray = ['www.nextcloud.com',
107
-						'www.google.com',
108
-						'www.github.com'];
109
-
110
-		foreach($siteArray as $site) {
111
-			if ($this->isSiteReachable($site)) {
112
-				return true;
113
-			}
114
-		}
115
-		return false;
116
-	}
117
-
118
-	/**
119
-	* Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
120
-	* @return bool
121
-	*/
122
-	private function isSiteReachable($sitename) {
123
-		$httpSiteName = 'http://' . $sitename . '/';
124
-		$httpsSiteName = 'https://' . $sitename . '/';
125
-
126
-		try {
127
-			$client = $this->clientService->newClient();
128
-			$client->get($httpSiteName);
129
-			$client->get($httpsSiteName);
130
-		} catch (\Exception $e) {
131
-			$this->logger->logException($e, ['app' => 'internet_connection_check']);
132
-			return false;
133
-		}
134
-		return true;
135
-	}
136
-
137
-	/**
138
-	 * Checks whether a local memcache is installed or not
139
-	 * @return bool
140
-	 */
141
-	private function isMemcacheConfigured() {
142
-		return $this->config->getSystemValue('memcache.local', null) !== null;
143
-	}
144
-
145
-	/**
146
-	 * Whether /dev/urandom is available to the PHP controller
147
-	 *
148
-	 * @return bool
149
-	 */
150
-	private function isUrandomAvailable() {
151
-		if(@file_exists('/dev/urandom')) {
152
-			$file = fopen('/dev/urandom', 'rb');
153
-			if($file) {
154
-				fclose($file);
155
-				return true;
156
-			}
157
-		}
158
-
159
-		return false;
160
-	}
161
-
162
-	/**
163
-	 * Public for the sake of unit-testing
164
-	 *
165
-	 * @return array
166
-	 */
167
-	protected function getCurlVersion() {
168
-		return curl_version();
169
-	}
170
-
171
-	/**
172
-	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
173
-	 * have multiple bugs which likely lead to problems in combination with
174
-	 * functionality required by ownCloud such as SNI.
175
-	 *
176
-	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
177
-	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
178
-	 * @return string
179
-	 */
180
-	private function isUsedTlsLibOutdated() {
181
-		// Don't run check when:
182
-		// 1. Server has `has_internet_connection` set to false
183
-		// 2. AppStore AND S2S is disabled
184
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
185
-			return '';
186
-		}
187
-		if(!$this->config->getSystemValue('appstoreenabled', true)
188
-			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
189
-			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
190
-			return '';
191
-		}
192
-
193
-		$versionString = $this->getCurlVersion();
194
-		if(isset($versionString['ssl_version'])) {
195
-			$versionString = $versionString['ssl_version'];
196
-		} else {
197
-			return '';
198
-		}
199
-
200
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
201
-		if(!$this->config->getSystemValue('appstoreenabled', true)) {
202
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
203
-		}
204
-
205
-		// Check if at least OpenSSL after 1.01d or 1.0.2b
206
-		if(strpos($versionString, 'OpenSSL/') === 0) {
207
-			$majorVersion = substr($versionString, 8, 5);
208
-			$patchRelease = substr($versionString, 13, 6);
209
-
210
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
211
-				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
212
-				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]);
213
-			}
214
-		}
215
-
216
-		// Check if NSS and perform heuristic check
217
-		if(strpos($versionString, 'NSS/') === 0) {
218
-			try {
219
-				$firstClient = $this->clientService->newClient();
220
-				$firstClient->get('https://nextcloud.com/');
221
-
222
-				$secondClient = $this->clientService->newClient();
223
-				$secondClient->get('https://nextcloud.com/');
224
-			} catch (ClientException $e) {
225
-				if($e->getResponse()->getStatusCode() === 400) {
226
-					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]);
227
-				}
228
-			}
229
-		}
230
-
231
-		return '';
232
-	}
233
-
234
-	/**
235
-	 * Whether the version is outdated
236
-	 *
237
-	 * @return bool
238
-	 */
239
-	protected function isPhpOutdated() {
240
-		if (version_compare(PHP_VERSION, '7.0.0', '<')) {
241
-			return true;
242
-		}
243
-
244
-		return false;
245
-	}
246
-
247
-	/**
248
-	 * Whether the php version is still supported (at time of release)
249
-	 * according to: https://secure.php.net/supported-versions.php
250
-	 *
251
-	 * @return array
252
-	 */
253
-	private function isPhpSupported() {
254
-		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
255
-	}
256
-
257
-	/**
258
-	 * Check if the reverse proxy configuration is working as expected
259
-	 *
260
-	 * @return bool
261
-	 */
262
-	private function forwardedForHeadersWorking() {
263
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
264
-		$remoteAddress = $this->request->getRemoteAddress();
265
-
266
-		if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
267
-			return false;
268
-		}
269
-
270
-		// either not enabled or working correctly
271
-		return true;
272
-	}
273
-
274
-	/**
275
-	 * Checks if the correct memcache module for PHP is installed. Only
276
-	 * fails if memcached is configured and the working module is not installed.
277
-	 *
278
-	 * @return bool
279
-	 */
280
-	private function isCorrectMemcachedPHPModuleInstalled() {
281
-		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
282
-			return true;
283
-		}
284
-
285
-		// there are two different memcached modules for PHP
286
-		// we only support memcached and not memcache
287
-		// https://code.google.com/p/memcached/wiki/PHPClientComparison
288
-		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
289
-	}
290
-
291
-	/**
292
-	 * Checks if set_time_limit is not disabled.
293
-	 *
294
-	 * @return bool
295
-	 */
296
-	private function isSettimelimitAvailable() {
297
-		if (function_exists('set_time_limit')
298
-			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
299
-			return true;
300
-		}
301
-
302
-		return false;
303
-	}
304
-
305
-	/**
306
-	 * @return RedirectResponse
307
-	 */
308
-	public function rescanFailedIntegrityCheck() {
309
-		$this->checker->runInstanceVerification();
310
-		return new RedirectResponse(
311
-			$this->urlGenerator->linkToRoute('settings.AdminSettings.index')
312
-		);
313
-	}
314
-
315
-	/**
316
-	 * @NoCSRFRequired
317
-	 * @return DataResponse
318
-	 */
319
-	public function getFailedIntegrityCheckFiles() {
320
-		if(!$this->checker->isCodeCheckEnforced()) {
321
-			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
322
-		}
323
-
324
-		$completeResults = $this->checker->getResults();
325
-
326
-		if(!empty($completeResults)) {
327
-			$formattedTextResponse = 'Technical information
52
+    /** @var IConfig */
53
+    private $config;
54
+    /** @var IClientService */
55
+    private $clientService;
56
+    /** @var \OC_Util */
57
+    private $util;
58
+    /** @var IURLGenerator */
59
+    private $urlGenerator;
60
+    /** @var IL10N */
61
+    private $l10n;
62
+    /** @var Checker */
63
+    private $checker;
64
+    /** @var ILogger */
65
+    private $logger;
66
+
67
+    /**
68
+     * @param string $AppName
69
+     * @param IRequest $request
70
+     * @param IConfig $config
71
+     * @param IClientService $clientService
72
+     * @param IURLGenerator $urlGenerator
73
+     * @param \OC_Util $util
74
+     * @param IL10N $l10n
75
+     * @param Checker $checker
76
+     * @param ILogger $logger
77
+     */
78
+    public function __construct($AppName,
79
+                                IRequest $request,
80
+                                IConfig $config,
81
+                                IClientService $clientService,
82
+                                IURLGenerator $urlGenerator,
83
+                                \OC_Util $util,
84
+                                IL10N $l10n,
85
+                                Checker $checker,
86
+                                ILogger $logger) {
87
+        parent::__construct($AppName, $request);
88
+        $this->config = $config;
89
+        $this->clientService = $clientService;
90
+        $this->util = $util;
91
+        $this->urlGenerator = $urlGenerator;
92
+        $this->l10n = $l10n;
93
+        $this->checker = $checker;
94
+        $this->logger = $logger;
95
+    }
96
+
97
+    /**
98
+     * Checks if the server can connect to the internet using HTTPS and HTTP
99
+     * @return bool
100
+     */
101
+    private function isInternetConnectionWorking() {
102
+        if ($this->config->getSystemValue('has_internet_connection', true) === false) {
103
+            return false;
104
+        }
105
+
106
+        $siteArray = ['www.nextcloud.com',
107
+                        'www.google.com',
108
+                        'www.github.com'];
109
+
110
+        foreach($siteArray as $site) {
111
+            if ($this->isSiteReachable($site)) {
112
+                return true;
113
+            }
114
+        }
115
+        return false;
116
+    }
117
+
118
+    /**
119
+     * Chceks if the ownCloud server can connect to a specific URL using both HTTPS and HTTP
120
+     * @return bool
121
+     */
122
+    private function isSiteReachable($sitename) {
123
+        $httpSiteName = 'http://' . $sitename . '/';
124
+        $httpsSiteName = 'https://' . $sitename . '/';
125
+
126
+        try {
127
+            $client = $this->clientService->newClient();
128
+            $client->get($httpSiteName);
129
+            $client->get($httpsSiteName);
130
+        } catch (\Exception $e) {
131
+            $this->logger->logException($e, ['app' => 'internet_connection_check']);
132
+            return false;
133
+        }
134
+        return true;
135
+    }
136
+
137
+    /**
138
+     * Checks whether a local memcache is installed or not
139
+     * @return bool
140
+     */
141
+    private function isMemcacheConfigured() {
142
+        return $this->config->getSystemValue('memcache.local', null) !== null;
143
+    }
144
+
145
+    /**
146
+     * Whether /dev/urandom is available to the PHP controller
147
+     *
148
+     * @return bool
149
+     */
150
+    private function isUrandomAvailable() {
151
+        if(@file_exists('/dev/urandom')) {
152
+            $file = fopen('/dev/urandom', 'rb');
153
+            if($file) {
154
+                fclose($file);
155
+                return true;
156
+            }
157
+        }
158
+
159
+        return false;
160
+    }
161
+
162
+    /**
163
+     * Public for the sake of unit-testing
164
+     *
165
+     * @return array
166
+     */
167
+    protected function getCurlVersion() {
168
+        return curl_version();
169
+    }
170
+
171
+    /**
172
+     * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
173
+     * have multiple bugs which likely lead to problems in combination with
174
+     * functionality required by ownCloud such as SNI.
175
+     *
176
+     * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
177
+     * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
178
+     * @return string
179
+     */
180
+    private function isUsedTlsLibOutdated() {
181
+        // Don't run check when:
182
+        // 1. Server has `has_internet_connection` set to false
183
+        // 2. AppStore AND S2S is disabled
184
+        if(!$this->config->getSystemValue('has_internet_connection', true)) {
185
+            return '';
186
+        }
187
+        if(!$this->config->getSystemValue('appstoreenabled', true)
188
+            && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
189
+            && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
190
+            return '';
191
+        }
192
+
193
+        $versionString = $this->getCurlVersion();
194
+        if(isset($versionString['ssl_version'])) {
195
+            $versionString = $versionString['ssl_version'];
196
+        } else {
197
+            return '';
198
+        }
199
+
200
+        $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
201
+        if(!$this->config->getSystemValue('appstoreenabled', true)) {
202
+            $features = (string)$this->l10n->t('Federated Cloud Sharing');
203
+        }
204
+
205
+        // Check if at least OpenSSL after 1.01d or 1.0.2b
206
+        if(strpos($versionString, 'OpenSSL/') === 0) {
207
+            $majorVersion = substr($versionString, 8, 5);
208
+            $patchRelease = substr($versionString, 13, 6);
209
+
210
+            if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
211
+                ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
212
+                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]);
213
+            }
214
+        }
215
+
216
+        // Check if NSS and perform heuristic check
217
+        if(strpos($versionString, 'NSS/') === 0) {
218
+            try {
219
+                $firstClient = $this->clientService->newClient();
220
+                $firstClient->get('https://nextcloud.com/');
221
+
222
+                $secondClient = $this->clientService->newClient();
223
+                $secondClient->get('https://nextcloud.com/');
224
+            } catch (ClientException $e) {
225
+                if($e->getResponse()->getStatusCode() === 400) {
226
+                    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]);
227
+                }
228
+            }
229
+        }
230
+
231
+        return '';
232
+    }
233
+
234
+    /**
235
+     * Whether the version is outdated
236
+     *
237
+     * @return bool
238
+     */
239
+    protected function isPhpOutdated() {
240
+        if (version_compare(PHP_VERSION, '7.0.0', '<')) {
241
+            return true;
242
+        }
243
+
244
+        return false;
245
+    }
246
+
247
+    /**
248
+     * Whether the php version is still supported (at time of release)
249
+     * according to: https://secure.php.net/supported-versions.php
250
+     *
251
+     * @return array
252
+     */
253
+    private function isPhpSupported() {
254
+        return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
255
+    }
256
+
257
+    /**
258
+     * Check if the reverse proxy configuration is working as expected
259
+     *
260
+     * @return bool
261
+     */
262
+    private function forwardedForHeadersWorking() {
263
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
264
+        $remoteAddress = $this->request->getRemoteAddress();
265
+
266
+        if (is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
267
+            return false;
268
+        }
269
+
270
+        // either not enabled or working correctly
271
+        return true;
272
+    }
273
+
274
+    /**
275
+     * Checks if the correct memcache module for PHP is installed. Only
276
+     * fails if memcached is configured and the working module is not installed.
277
+     *
278
+     * @return bool
279
+     */
280
+    private function isCorrectMemcachedPHPModuleInstalled() {
281
+        if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
282
+            return true;
283
+        }
284
+
285
+        // there are two different memcached modules for PHP
286
+        // we only support memcached and not memcache
287
+        // https://code.google.com/p/memcached/wiki/PHPClientComparison
288
+        return !(!extension_loaded('memcached') && extension_loaded('memcache'));
289
+    }
290
+
291
+    /**
292
+     * Checks if set_time_limit is not disabled.
293
+     *
294
+     * @return bool
295
+     */
296
+    private function isSettimelimitAvailable() {
297
+        if (function_exists('set_time_limit')
298
+            && strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
299
+            return true;
300
+        }
301
+
302
+        return false;
303
+    }
304
+
305
+    /**
306
+     * @return RedirectResponse
307
+     */
308
+    public function rescanFailedIntegrityCheck() {
309
+        $this->checker->runInstanceVerification();
310
+        return new RedirectResponse(
311
+            $this->urlGenerator->linkToRoute('settings.AdminSettings.index')
312
+        );
313
+    }
314
+
315
+    /**
316
+     * @NoCSRFRequired
317
+     * @return DataResponse
318
+     */
319
+    public function getFailedIntegrityCheckFiles() {
320
+        if(!$this->checker->isCodeCheckEnforced()) {
321
+            return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
322
+        }
323
+
324
+        $completeResults = $this->checker->getResults();
325
+
326
+        if(!empty($completeResults)) {
327
+            $formattedTextResponse = 'Technical information
328 328
 =====================
329 329
 The following list covers which files have failed the integrity check. Please read
330 330
 the previous linked documentation to learn more about the errors and how to fix
@@ -333,112 +333,112 @@  discard block
 block discarded – undo
333 333
 Results
334 334
 =======
335 335
 ';
336
-			foreach($completeResults as $context => $contextResult) {
337
-				$formattedTextResponse .= "- $context\n";
338
-
339
-				foreach($contextResult as $category => $result) {
340
-					$formattedTextResponse .= "\t- $category\n";
341
-					if($category !== 'EXCEPTION') {
342
-						foreach ($result as $key => $results) {
343
-							$formattedTextResponse .= "\t\t- $key\n";
344
-						}
345
-					} else {
346
-						foreach ($result as $key => $results) {
347
-							$formattedTextResponse .= "\t\t- $results\n";
348
-						}
349
-					}
350
-
351
-				}
352
-			}
353
-
354
-			$formattedTextResponse .= '
336
+            foreach($completeResults as $context => $contextResult) {
337
+                $formattedTextResponse .= "- $context\n";
338
+
339
+                foreach($contextResult as $category => $result) {
340
+                    $formattedTextResponse .= "\t- $category\n";
341
+                    if($category !== 'EXCEPTION') {
342
+                        foreach ($result as $key => $results) {
343
+                            $formattedTextResponse .= "\t\t- $key\n";
344
+                        }
345
+                    } else {
346
+                        foreach ($result as $key => $results) {
347
+                            $formattedTextResponse .= "\t\t- $results\n";
348
+                        }
349
+                    }
350
+
351
+                }
352
+            }
353
+
354
+            $formattedTextResponse .= '
355 355
 Raw output
356 356
 ==========
357 357
 ';
358
-			$formattedTextResponse .= print_r($completeResults, true);
359
-		} else {
360
-			$formattedTextResponse = 'No errors have been found.';
361
-		}
362
-
363
-
364
-		$response = new DataDisplayResponse(
365
-			$formattedTextResponse,
366
-			Http::STATUS_OK,
367
-			[
368
-				'Content-Type' => 'text/plain',
369
-			]
370
-		);
371
-
372
-		return $response;
373
-	}
374
-
375
-	/**
376
-	 * Checks whether a PHP opcache is properly set up
377
-	 * @return bool
378
-	 */
379
-	protected function isOpcacheProperlySetup() {
380
-		$iniWrapper = new IniGetWrapper();
381
-
382
-		$isOpcacheProperlySetUp = true;
383
-
384
-		if(!$iniWrapper->getBool('opcache.enable')) {
385
-			$isOpcacheProperlySetUp = false;
386
-		}
387
-
388
-		if(!$iniWrapper->getBool('opcache.save_comments')) {
389
-			$isOpcacheProperlySetUp = false;
390
-		}
391
-
392
-		if(!$iniWrapper->getBool('opcache.enable_cli')) {
393
-			$isOpcacheProperlySetUp = false;
394
-		}
395
-
396
-		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
397
-			$isOpcacheProperlySetUp = false;
398
-		}
399
-
400
-		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
401
-			$isOpcacheProperlySetUp = false;
402
-		}
403
-
404
-		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
405
-			$isOpcacheProperlySetUp = false;
406
-		}
407
-
408
-		return $isOpcacheProperlySetUp;
409
-	}
410
-
411
-	/**
412
-	 * Check if the required FreeType functions are present
413
-	 * @return bool
414
-	 */
415
-	protected function hasFreeTypeSupport() {
416
-		return function_exists('imagettfbbox') && function_exists('imagettftext');
417
-	}
418
-
419
-	/**
420
-	 * @return DataResponse
421
-	 */
422
-	public function check() {
423
-		return new DataResponse(
424
-			[
425
-				'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
426
-				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
427
-				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
428
-				'isUrandomAvailable' => $this->isUrandomAvailable(),
429
-				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
430
-				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
431
-				'phpSupported' => $this->isPhpSupported(),
432
-				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
433
-				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
434
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
435
-				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
436
-				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
437
-				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
438
-				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
439
-				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
440
-				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
441
-			]
442
-		);
443
-	}
358
+            $formattedTextResponse .= print_r($completeResults, true);
359
+        } else {
360
+            $formattedTextResponse = 'No errors have been found.';
361
+        }
362
+
363
+
364
+        $response = new DataDisplayResponse(
365
+            $formattedTextResponse,
366
+            Http::STATUS_OK,
367
+            [
368
+                'Content-Type' => 'text/plain',
369
+            ]
370
+        );
371
+
372
+        return $response;
373
+    }
374
+
375
+    /**
376
+     * Checks whether a PHP opcache is properly set up
377
+     * @return bool
378
+     */
379
+    protected function isOpcacheProperlySetup() {
380
+        $iniWrapper = new IniGetWrapper();
381
+
382
+        $isOpcacheProperlySetUp = true;
383
+
384
+        if(!$iniWrapper->getBool('opcache.enable')) {
385
+            $isOpcacheProperlySetUp = false;
386
+        }
387
+
388
+        if(!$iniWrapper->getBool('opcache.save_comments')) {
389
+            $isOpcacheProperlySetUp = false;
390
+        }
391
+
392
+        if(!$iniWrapper->getBool('opcache.enable_cli')) {
393
+            $isOpcacheProperlySetUp = false;
394
+        }
395
+
396
+        if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
397
+            $isOpcacheProperlySetUp = false;
398
+        }
399
+
400
+        if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
401
+            $isOpcacheProperlySetUp = false;
402
+        }
403
+
404
+        if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
405
+            $isOpcacheProperlySetUp = false;
406
+        }
407
+
408
+        return $isOpcacheProperlySetUp;
409
+    }
410
+
411
+    /**
412
+     * Check if the required FreeType functions are present
413
+     * @return bool
414
+     */
415
+    protected function hasFreeTypeSupport() {
416
+        return function_exists('imagettfbbox') && function_exists('imagettftext');
417
+    }
418
+
419
+    /**
420
+     * @return DataResponse
421
+     */
422
+    public function check() {
423
+        return new DataResponse(
424
+            [
425
+                'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
426
+                'isMemcacheConfigured' => $this->isMemcacheConfigured(),
427
+                'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
428
+                'isUrandomAvailable' => $this->isUrandomAvailable(),
429
+                'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
430
+                'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
431
+                'phpSupported' => $this->isPhpSupported(),
432
+                'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
433
+                'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
434
+                'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
435
+                'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
436
+                'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
437
+                'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
438
+                'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
439
+                'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
440
+                'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
441
+            ]
442
+        );
443
+    }
444 444
 }
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.
Spacing   +154 added lines, -154 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;
@@ -690,10 +690,10 @@  discard block
 block discarded – undo
690 690
 			if ($settingsFound === true) {
691 691
 				$config = array(
692 692
 					'ldapPort' => $p,
693
-					'ldapTLS' => (int)$t
693
+					'ldapTLS' => (int) $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 ' . (int)$tls, \OCP\Util::DEBUG);
1072
+			\OCP\Util::writeLog('user_ldap', 'Wiz: Bind successful to Port '.$port.' TLS '.(int) $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
 		}
@@ -1326,18 +1326,18 @@  discard block
 block discarded – undo
1326 1326
 		//636 ← LDAPS / SSL
1327 1327
 		//7xxx ← UCS. need to be checked first, because both ports may be open
1328 1328
 		$host = $this->configuration->ldapHost;
1329
-		$port = (int)$this->configuration->ldapPort;
1329
+		$port = (int) $this->configuration->ldapPort;
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.
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' => (int)$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 ' . (int)$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 = (int)$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' => (int)$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 ' . (int)$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 = (int)$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.
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.
Indentation   +226 added lines, -226 removed lines patch added patch discarded remove patch
@@ -37,230 +37,230 @@
 block discarded – undo
37 37
  * Shared mount points can be moved by the user
38 38
  */
39 39
 class SharedMount extends MountPoint implements MoveableMount {
40
-	/**
41
-	 * @var \OCA\Files_Sharing\SharedStorage $storage
42
-	 */
43
-	protected $storage = null;
44
-
45
-	/**
46
-	 * @var \OC\Files\View
47
-	 */
48
-	private $recipientView;
49
-
50
-	/**
51
-	 * @var string
52
-	 */
53
-	private $user;
54
-
55
-	/** @var \OCP\Share\IShare */
56
-	private $superShare;
57
-
58
-	/** @var \OCP\Share\IShare[] */
59
-	private $groupedShares;
60
-
61
-	/**
62
-	 * @param string $storage
63
-	 * @param SharedMount[] $mountpoints
64
-	 * @param array|null $arguments
65
-	 * @param \OCP\Files\Storage\IStorageFactory $loader
66
-	 */
67
-	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
68
-		$this->user = $arguments['user'];
69
-		$this->recipientView = new View('/' . $this->user . '/files');
70
-
71
-		$this->superShare = $arguments['superShare'];
72
-		$this->groupedShares = $arguments['groupedShares'];
73
-
74
-		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
75
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
76
-		$arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
77
-		parent::__construct($storage, $absMountPoint, $arguments, $loader);
78
-	}
79
-
80
-	/**
81
-	 * check if the parent folder exists otherwise move the mount point up
82
-	 *
83
-	 * @param \OCP\Share\IShare $share
84
-	 * @param SharedMount[] $mountpoints
85
-	 * @return string
86
-	 */
87
-	private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints) {
88
-
89
-		$mountPoint = basename($share->getTarget());
90
-		$parent = dirname($share->getTarget());
91
-
92
-		if (!$this->recipientView->is_dir($parent)) {
93
-			$parent = Helper::getShareFolder($this->recipientView);
94
-		}
95
-
96
-		$newMountPoint = $this->generateUniqueTarget(
97
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
98
-			$this->recipientView,
99
-			$mountpoints
100
-		);
101
-
102
-		if ($newMountPoint !== $share->getTarget()) {
103
-			$this->updateFileTarget($newMountPoint, $share);
104
-		}
105
-
106
-		return $newMountPoint;
107
-	}
108
-
109
-	/**
110
-	 * update fileTarget in the database if the mount point changed
111
-	 *
112
-	 * @param string $newPath
113
-	 * @param \OCP\Share\IShare $share
114
-	 * @return bool
115
-	 */
116
-	private function updateFileTarget($newPath, &$share) {
117
-		$share->setTarget($newPath);
118
-
119
-		foreach ($this->groupedShares as $tmpShare) {
120
-			$tmpShare->setTarget($newPath);
121
-			\OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
122
-		}
123
-	}
124
-
125
-
126
-	/**
127
-	 * @param string $path
128
-	 * @param View $view
129
-	 * @param SharedMount[] $mountpoints
130
-	 * @return mixed
131
-	 */
132
-	private function generateUniqueTarget($path, $view, array $mountpoints) {
133
-		$pathinfo = pathinfo($path);
134
-		$ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
135
-		$name = $pathinfo['filename'];
136
-		$dir = $pathinfo['dirname'];
137
-
138
-		// Helper function to find existing mount points
139
-		$mountpointExists = function ($path) use ($mountpoints) {
140
-			foreach ($mountpoints as $mountpoint) {
141
-				if ($mountpoint->getShare()->getTarget() === $path) {
142
-					return true;
143
-				}
144
-			}
145
-			return false;
146
-		};
147
-
148
-		$i = 2;
149
-		while ($view->file_exists($path) || $mountpointExists($path)) {
150
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
151
-			$i++;
152
-		}
153
-
154
-		return $path;
155
-	}
156
-
157
-	/**
158
-	 * Format a path to be relative to the /user/files/ directory
159
-	 *
160
-	 * @param string $path the absolute path
161
-	 * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
162
-	 * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
163
-	 */
164
-	protected function stripUserFilesPath($path) {
165
-		$trimmed = ltrim($path, '/');
166
-		$split = explode('/', $trimmed);
167
-
168
-		// it is not a file relative to data/user/files
169
-		if (count($split) < 3 || $split[1] !== 'files') {
170
-			\OCP\Util::writeLog('file sharing',
171
-				'Can not strip userid and "files/" from path: ' . $path,
172
-				\OCP\Util::ERROR);
173
-			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
174
-		}
175
-
176
-		// skip 'user' and 'files'
177
-		$sliced = array_slice($split, 2);
178
-		$relPath = implode('/', $sliced);
179
-
180
-		return '/' . $relPath;
181
-	}
182
-
183
-	/**
184
-	 * Move the mount point to $target
185
-	 *
186
-	 * @param string $target the target mount point
187
-	 * @return bool
188
-	 */
189
-	public function moveMount($target) {
190
-
191
-		$relTargetPath = $this->stripUserFilesPath($target);
192
-		$share = $this->storage->getShare();
193
-
194
-		$result = true;
195
-
196
-		try {
197
-			$this->updateFileTarget($relTargetPath, $share);
198
-			$this->setMountPoint($target);
199
-			$this->storage->setMountPoint($relTargetPath);
200
-		} catch (\Exception $e) {
201
-			\OCP\Util::writeLog('file sharing',
202
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
203
-				\OCP\Util::ERROR);
204
-		}
205
-
206
-		return $result;
207
-	}
208
-
209
-	/**
210
-	 * Remove the mount points
211
-	 *
212
-	 * @return bool
213
-	 */
214
-	public function removeMount() {
215
-		$mountManager = \OC\Files\Filesystem::getMountManager();
216
-		/** @var $storage \OCA\Files_Sharing\SharedStorage */
217
-		$storage = $this->getStorage();
218
-		$result = $storage->unshareStorage();
219
-		$mountManager->removeMount($this->mountPoint);
220
-
221
-		return $result;
222
-	}
223
-
224
-	/**
225
-	 * @return \OCP\Share\IShare
226
-	 */
227
-	public function getShare() {
228
-		return $this->superShare;
229
-	}
230
-
231
-	/**
232
-	 * Get the file id of the root of the storage
233
-	 *
234
-	 * @return int
235
-	 */
236
-	public function getStorageRootId() {
237
-		return $this->getShare()->getNodeId();
238
-	}
239
-
240
-	/**
241
-	 * @return int
242
-	 */
243
-	public function getNumericStorageId() {
244
-		if (!is_null($this->getShare()->getNodeCacheEntry())) {
245
-			return $this->getShare()->getNodeCacheEntry()->getStorageId();
246
-		} else {
247
-			$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
248
-
249
-			$query = $builder->select('storage')
250
-				->from('filecache')
251
-				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
252
-
253
-			$result = $query->execute();
254
-			$row = $result->fetch();
255
-			$result->closeCursor();
256
-			if ($row) {
257
-				return (int)$row['storage'];
258
-			}
259
-			return -1;
260
-		}
261
-	}
262
-
263
-	public function getMountType() {
264
-		return 'shared';
265
-	}
40
+    /**
41
+     * @var \OCA\Files_Sharing\SharedStorage $storage
42
+     */
43
+    protected $storage = null;
44
+
45
+    /**
46
+     * @var \OC\Files\View
47
+     */
48
+    private $recipientView;
49
+
50
+    /**
51
+     * @var string
52
+     */
53
+    private $user;
54
+
55
+    /** @var \OCP\Share\IShare */
56
+    private $superShare;
57
+
58
+    /** @var \OCP\Share\IShare[] */
59
+    private $groupedShares;
60
+
61
+    /**
62
+     * @param string $storage
63
+     * @param SharedMount[] $mountpoints
64
+     * @param array|null $arguments
65
+     * @param \OCP\Files\Storage\IStorageFactory $loader
66
+     */
67
+    public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
68
+        $this->user = $arguments['user'];
69
+        $this->recipientView = new View('/' . $this->user . '/files');
70
+
71
+        $this->superShare = $arguments['superShare'];
72
+        $this->groupedShares = $arguments['groupedShares'];
73
+
74
+        $newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
75
+        $absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
76
+        $arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
77
+        parent::__construct($storage, $absMountPoint, $arguments, $loader);
78
+    }
79
+
80
+    /**
81
+     * check if the parent folder exists otherwise move the mount point up
82
+     *
83
+     * @param \OCP\Share\IShare $share
84
+     * @param SharedMount[] $mountpoints
85
+     * @return string
86
+     */
87
+    private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints) {
88
+
89
+        $mountPoint = basename($share->getTarget());
90
+        $parent = dirname($share->getTarget());
91
+
92
+        if (!$this->recipientView->is_dir($parent)) {
93
+            $parent = Helper::getShareFolder($this->recipientView);
94
+        }
95
+
96
+        $newMountPoint = $this->generateUniqueTarget(
97
+            \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
98
+            $this->recipientView,
99
+            $mountpoints
100
+        );
101
+
102
+        if ($newMountPoint !== $share->getTarget()) {
103
+            $this->updateFileTarget($newMountPoint, $share);
104
+        }
105
+
106
+        return $newMountPoint;
107
+    }
108
+
109
+    /**
110
+     * update fileTarget in the database if the mount point changed
111
+     *
112
+     * @param string $newPath
113
+     * @param \OCP\Share\IShare $share
114
+     * @return bool
115
+     */
116
+    private function updateFileTarget($newPath, &$share) {
117
+        $share->setTarget($newPath);
118
+
119
+        foreach ($this->groupedShares as $tmpShare) {
120
+            $tmpShare->setTarget($newPath);
121
+            \OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
122
+        }
123
+    }
124
+
125
+
126
+    /**
127
+     * @param string $path
128
+     * @param View $view
129
+     * @param SharedMount[] $mountpoints
130
+     * @return mixed
131
+     */
132
+    private function generateUniqueTarget($path, $view, array $mountpoints) {
133
+        $pathinfo = pathinfo($path);
134
+        $ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
135
+        $name = $pathinfo['filename'];
136
+        $dir = $pathinfo['dirname'];
137
+
138
+        // Helper function to find existing mount points
139
+        $mountpointExists = function ($path) use ($mountpoints) {
140
+            foreach ($mountpoints as $mountpoint) {
141
+                if ($mountpoint->getShare()->getTarget() === $path) {
142
+                    return true;
143
+                }
144
+            }
145
+            return false;
146
+        };
147
+
148
+        $i = 2;
149
+        while ($view->file_exists($path) || $mountpointExists($path)) {
150
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
151
+            $i++;
152
+        }
153
+
154
+        return $path;
155
+    }
156
+
157
+    /**
158
+     * Format a path to be relative to the /user/files/ directory
159
+     *
160
+     * @param string $path the absolute path
161
+     * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
162
+     * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
163
+     */
164
+    protected function stripUserFilesPath($path) {
165
+        $trimmed = ltrim($path, '/');
166
+        $split = explode('/', $trimmed);
167
+
168
+        // it is not a file relative to data/user/files
169
+        if (count($split) < 3 || $split[1] !== 'files') {
170
+            \OCP\Util::writeLog('file sharing',
171
+                'Can not strip userid and "files/" from path: ' . $path,
172
+                \OCP\Util::ERROR);
173
+            throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
174
+        }
175
+
176
+        // skip 'user' and 'files'
177
+        $sliced = array_slice($split, 2);
178
+        $relPath = implode('/', $sliced);
179
+
180
+        return '/' . $relPath;
181
+    }
182
+
183
+    /**
184
+     * Move the mount point to $target
185
+     *
186
+     * @param string $target the target mount point
187
+     * @return bool
188
+     */
189
+    public function moveMount($target) {
190
+
191
+        $relTargetPath = $this->stripUserFilesPath($target);
192
+        $share = $this->storage->getShare();
193
+
194
+        $result = true;
195
+
196
+        try {
197
+            $this->updateFileTarget($relTargetPath, $share);
198
+            $this->setMountPoint($target);
199
+            $this->storage->setMountPoint($relTargetPath);
200
+        } catch (\Exception $e) {
201
+            \OCP\Util::writeLog('file sharing',
202
+                'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
203
+                \OCP\Util::ERROR);
204
+        }
205
+
206
+        return $result;
207
+    }
208
+
209
+    /**
210
+     * Remove the mount points
211
+     *
212
+     * @return bool
213
+     */
214
+    public function removeMount() {
215
+        $mountManager = \OC\Files\Filesystem::getMountManager();
216
+        /** @var $storage \OCA\Files_Sharing\SharedStorage */
217
+        $storage = $this->getStorage();
218
+        $result = $storage->unshareStorage();
219
+        $mountManager->removeMount($this->mountPoint);
220
+
221
+        return $result;
222
+    }
223
+
224
+    /**
225
+     * @return \OCP\Share\IShare
226
+     */
227
+    public function getShare() {
228
+        return $this->superShare;
229
+    }
230
+
231
+    /**
232
+     * Get the file id of the root of the storage
233
+     *
234
+     * @return int
235
+     */
236
+    public function getStorageRootId() {
237
+        return $this->getShare()->getNodeId();
238
+    }
239
+
240
+    /**
241
+     * @return int
242
+     */
243
+    public function getNumericStorageId() {
244
+        if (!is_null($this->getShare()->getNodeCacheEntry())) {
245
+            return $this->getShare()->getNodeCacheEntry()->getStorageId();
246
+        } else {
247
+            $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
248
+
249
+            $query = $builder->select('storage')
250
+                ->from('filecache')
251
+                ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
252
+
253
+            $result = $query->execute();
254
+            $row = $result->fetch();
255
+            $result->closeCursor();
256
+            if ($row) {
257
+                return (int)$row['storage'];
258
+            }
259
+            return -1;
260
+        }
261
+    }
262
+
263
+    public function getMountType() {
264
+        return 'shared';
265
+    }
266 266
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -66,14 +66,14 @@  discard block
 block discarded – undo
66 66
 	 */
67 67
 	public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) {
68 68
 		$this->user = $arguments['user'];
69
-		$this->recipientView = new View('/' . $this->user . '/files');
69
+		$this->recipientView = new View('/'.$this->user.'/files');
70 70
 
71 71
 		$this->superShare = $arguments['superShare'];
72 72
 		$this->groupedShares = $arguments['groupedShares'];
73 73
 
74 74
 		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints);
75
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
76
-		$arguments['ownerView'] = new View('/' . $this->superShare->getShareOwner() . '/files');
75
+		$absMountPoint = '/'.$this->user.'/files'.$newMountPoint;
76
+		$arguments['ownerView'] = new View('/'.$this->superShare->getShareOwner().'/files');
77 77
 		parent::__construct($storage, $absMountPoint, $arguments, $loader);
78 78
 	}
79 79
 
@@ -94,7 +94,7 @@  discard block
 block discarded – undo
94 94
 		}
95 95
 
96 96
 		$newMountPoint = $this->generateUniqueTarget(
97
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
97
+			\OC\Files\Filesystem::normalizePath($parent.'/'.$mountPoint),
98 98
 			$this->recipientView,
99 99
 			$mountpoints
100 100
 		);
@@ -131,12 +131,12 @@  discard block
 block discarded – undo
131 131
 	 */
132 132
 	private function generateUniqueTarget($path, $view, array $mountpoints) {
133 133
 		$pathinfo = pathinfo($path);
134
-		$ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
134
+		$ext = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
135 135
 		$name = $pathinfo['filename'];
136 136
 		$dir = $pathinfo['dirname'];
137 137
 
138 138
 		// Helper function to find existing mount points
139
-		$mountpointExists = function ($path) use ($mountpoints) {
139
+		$mountpointExists = function($path) use ($mountpoints) {
140 140
 			foreach ($mountpoints as $mountpoint) {
141 141
 				if ($mountpoint->getShare()->getTarget() === $path) {
142 142
 					return true;
@@ -147,7 +147,7 @@  discard block
 block discarded – undo
147 147
 
148 148
 		$i = 2;
149 149
 		while ($view->file_exists($path) || $mountpointExists($path)) {
150
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
150
+			$path = Filesystem::normalizePath($dir.'/'.$name.' ('.$i.')'.$ext);
151 151
 			$i++;
152 152
 		}
153 153
 
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 		// it is not a file relative to data/user/files
169 169
 		if (count($split) < 3 || $split[1] !== 'files') {
170 170
 			\OCP\Util::writeLog('file sharing',
171
-				'Can not strip userid and "files/" from path: ' . $path,
171
+				'Can not strip userid and "files/" from path: '.$path,
172 172
 				\OCP\Util::ERROR);
173 173
 			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
174 174
 		}
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 		$sliced = array_slice($split, 2);
178 178
 		$relPath = implode('/', $sliced);
179 179
 
180
-		return '/' . $relPath;
180
+		return '/'.$relPath;
181 181
 	}
182 182
 
183 183
 	/**
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
 			$this->storage->setMountPoint($relTargetPath);
200 200
 		} catch (\Exception $e) {
201 201
 			\OCP\Util::writeLog('file sharing',
202
-				'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"',
202
+				'Could not rename mount point for shared folder "'.$this->getMountPoint().'" to "'.$target.'"',
203 203
 				\OCP\Util::ERROR);
204 204
 		}
205 205
 
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
 			$row = $result->fetch();
255 255
 			$result->closeCursor();
256 256
 			if ($row) {
257
-				return (int)$row['storage'];
257
+				return (int) $row['storage'];
258 258
 			}
259 259
 			return -1;
260 260
 		}
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.
core/Controller/LostController.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -132,7 +132,7 @@
 block discarded – undo
132 132
 	}
133 133
 
134 134
 	/**
135
-	 * @param $message
135
+	 * @param string $message
136 136
 	 * @param array $additional
137 137
 	 * @return array
138 138
 	 */
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -167,7 +167,7 @@  discard block
 block discarded – undo
167 167
 	 */
168 168
 	protected function checkPasswordResetToken($token, $userId) {
169 169
 		$user = $this->userManager->get($userId);
170
-		if($user === null || !$user->isEnabled()) {
170
+		if ($user === null || !$user->isEnabled()) {
171 171
 			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
172 172
 		}
173 173
 
@@ -180,11 +180,11 @@  discard block
 block discarded – undo
180 180
 		}
181 181
 
182 182
 		$splittedToken = explode(':', $decryptedToken);
183
-		if(count($splittedToken) !== 2) {
183
+		if (count($splittedToken) !== 2) {
184 184
 			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
185 185
 		}
186 186
 
187
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
187
+		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60 * 60 * 12) ||
188 188
 			$user->getLastLogin() > $splittedToken[0]) {
189 189
 			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
190 190
 		}
@@ -199,7 +199,7 @@  discard block
 block discarded – undo
199 199
 	 * @param array $additional
200 200
 	 * @return array
201 201
 	 */
202
-	private function error($message, array $additional=array()) {
202
+	private function error($message, array $additional = array()) {
203 203
 		return array_merge(array('status' => 'error', 'msg' => $message), $additional);
204 204
 	}
205 205
 
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
 	 * @param string $user
219 219
 	 * @return JSONResponse
220 220
 	 */
221
-	public function email($user){
221
+	public function email($user) {
222 222
 		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
223 223
 			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
224 224
 		}
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 		// FIXME: use HTTP error codes
227 227
 		try {
228 228
 			$this->sendEmail($user);
229
-		} catch (\Exception $e){
229
+		} catch (\Exception $e) {
230 230
 			$response = new JSONResponse($this->error($e->getMessage()));
231 231
 			$response->throttle();
232 232
 			return $response;
@@ -268,7 +268,7 @@  discard block
 block discarded – undo
268 268
 
269 269
 			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
270 270
 			@\OC::$server->getUserSession()->unsetMagicInCookie();
271
-		} catch (\Exception $e){
271
+		} catch (\Exception $e) {
272 272
 			return $this->error($e->getMessage());
273 273
 		}
274 274
 
@@ -299,8 +299,8 @@  discard block
 block discarded – undo
299 299
 			ISecureRandom::CHAR_LOWER.
300 300
 			ISecureRandom::CHAR_UPPER
301 301
 		);
302
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
303
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
302
+		$tokenValue = $this->timeFactory->getTime().':'.$token;
303
+		$encryptedValue = $this->crypto->encrypt($tokenValue, $email.$this->config->getSystemValue('secret'));
304 304
 		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
305 305
 
306 306
 		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
Please login to merge, or discard this patch.
Indentation   +315 added lines, -315 removed lines patch added patch discarded remove patch
@@ -56,319 +56,319 @@
 block discarded – undo
56 56
  */
57 57
 class LostController extends Controller {
58 58
 
59
-	/** @var IURLGenerator */
60
-	protected $urlGenerator;
61
-	/** @var IUserManager */
62
-	protected $userManager;
63
-	/** @var Defaults */
64
-	protected $defaults;
65
-	/** @var IL10N */
66
-	protected $l10n;
67
-	/** @var string */
68
-	protected $from;
69
-	/** @var IManager */
70
-	protected $encryptionManager;
71
-	/** @var IConfig */
72
-	protected $config;
73
-	/** @var ISecureRandom */
74
-	protected $secureRandom;
75
-	/** @var IMailer */
76
-	protected $mailer;
77
-	/** @var ITimeFactory */
78
-	protected $timeFactory;
79
-	/** @var ICrypto */
80
-	protected $crypto;
81
-
82
-	/**
83
-	 * @param string $appName
84
-	 * @param IRequest $request
85
-	 * @param IURLGenerator $urlGenerator
86
-	 * @param IUserManager $userManager
87
-	 * @param Defaults $defaults
88
-	 * @param IL10N $l10n
89
-	 * @param IConfig $config
90
-	 * @param ISecureRandom $secureRandom
91
-	 * @param string $defaultMailAddress
92
-	 * @param IManager $encryptionManager
93
-	 * @param IMailer $mailer
94
-	 * @param ITimeFactory $timeFactory
95
-	 * @param ICrypto $crypto
96
-	 */
97
-	public function __construct($appName,
98
-								IRequest $request,
99
-								IURLGenerator $urlGenerator,
100
-								IUserManager $userManager,
101
-								Defaults $defaults,
102
-								IL10N $l10n,
103
-								IConfig $config,
104
-								ISecureRandom $secureRandom,
105
-								$defaultMailAddress,
106
-								IManager $encryptionManager,
107
-								IMailer $mailer,
108
-								ITimeFactory $timeFactory,
109
-								ICrypto $crypto) {
110
-		parent::__construct($appName, $request);
111
-		$this->urlGenerator = $urlGenerator;
112
-		$this->userManager = $userManager;
113
-		$this->defaults = $defaults;
114
-		$this->l10n = $l10n;
115
-		$this->secureRandom = $secureRandom;
116
-		$this->from = $defaultMailAddress;
117
-		$this->encryptionManager = $encryptionManager;
118
-		$this->config = $config;
119
-		$this->mailer = $mailer;
120
-		$this->timeFactory = $timeFactory;
121
-		$this->crypto = $crypto;
122
-	}
123
-
124
-	/**
125
-	 * Someone wants to reset their password:
126
-	 *
127
-	 * @PublicPage
128
-	 * @NoCSRFRequired
129
-	 *
130
-	 * @param string $token
131
-	 * @param string $userId
132
-	 * @return TemplateResponse
133
-	 */
134
-	public function resetform($token, $userId) {
135
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
136
-			return new TemplateResponse('core', 'error', [
137
-					'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
138
-				],
139
-				'guest'
140
-			);
141
-		}
142
-
143
-		try {
144
-			$this->checkPasswordResetToken($token, $userId);
145
-		} catch (\Exception $e) {
146
-			return new TemplateResponse(
147
-				'core', 'error', [
148
-					"errors" => array(array("error" => $e->getMessage()))
149
-				],
150
-				'guest'
151
-			);
152
-		}
153
-
154
-		return new TemplateResponse(
155
-			'core',
156
-			'lostpassword/resetpassword',
157
-			array(
158
-				'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
159
-			),
160
-			'guest'
161
-		);
162
-	}
163
-
164
-	/**
165
-	 * @param string $token
166
-	 * @param string $userId
167
-	 * @throws \Exception
168
-	 */
169
-	protected function checkPasswordResetToken($token, $userId) {
170
-		$user = $this->userManager->get($userId);
171
-		if($user === null || !$user->isEnabled()) {
172
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
173
-		}
174
-
175
-		try {
176
-			$encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
177
-			$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
178
-			$decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
179
-		} catch (\Exception $e) {
180
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
181
-		}
182
-
183
-		$splittedToken = explode(':', $decryptedToken);
184
-		if(count($splittedToken) !== 2) {
185
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
186
-		}
187
-
188
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
189
-			$user->getLastLogin() > $splittedToken[0]) {
190
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
191
-		}
192
-
193
-		if (!hash_equals($splittedToken[1], $token)) {
194
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
195
-		}
196
-	}
197
-
198
-	/**
199
-	 * @param $message
200
-	 * @param array $additional
201
-	 * @return array
202
-	 */
203
-	private function error($message, array $additional=array()) {
204
-		return array_merge(array('status' => 'error', 'msg' => $message), $additional);
205
-	}
206
-
207
-	/**
208
-	 * @return array
209
-	 */
210
-	private function success() {
211
-		return array('status'=>'success');
212
-	}
213
-
214
-	/**
215
-	 * @PublicPage
216
-	 * @BruteForceProtection(action=passwordResetEmail)
217
-	 * @AnonRateThrottle(limit=10, period=300)
218
-	 *
219
-	 * @param string $user
220
-	 * @return JSONResponse
221
-	 */
222
-	public function email($user){
223
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
224
-			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
225
-		}
226
-
227
-		\OCP\Util::emitHook(
228
-			'\OCA\Files_Sharing\API\Server2Server',
229
-			'preLoginNameUsedAsUserName',
230
-			['uid' => &$user]
231
-		);
232
-
233
-		// FIXME: use HTTP error codes
234
-		try {
235
-			$this->sendEmail($user);
236
-		} catch (\Exception $e){
237
-			$response = new JSONResponse($this->error($e->getMessage()));
238
-			$response->throttle();
239
-			return $response;
240
-		}
241
-
242
-		$response = new JSONResponse($this->success());
243
-		$response->throttle();
244
-		return $response;
245
-	}
246
-
247
-	/**
248
-	 * @PublicPage
249
-	 * @param string $token
250
-	 * @param string $userId
251
-	 * @param string $password
252
-	 * @param boolean $proceed
253
-	 * @return array
254
-	 */
255
-	public function setPassword($token, $userId, $password, $proceed) {
256
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
257
-			return $this->error($this->l10n->t('Password reset is disabled'));
258
-		}
259
-
260
-		if ($this->encryptionManager->isEnabled() && !$proceed) {
261
-			return $this->error('', array('encryption' => true));
262
-		}
263
-
264
-		try {
265
-			$this->checkPasswordResetToken($token, $userId);
266
-			$user = $this->userManager->get($userId);
267
-
268
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
269
-
270
-			if (!$user->setPassword($password)) {
271
-				throw new \Exception();
272
-			}
273
-
274
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
275
-
276
-			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
277
-			@\OC::$server->getUserSession()->unsetMagicInCookie();
278
-		} catch (\Exception $e){
279
-			return $this->error($e->getMessage());
280
-		}
281
-
282
-		return $this->success();
283
-	}
284
-
285
-	/**
286
-	 * @param string $input
287
-	 * @throws \Exception
288
-	 */
289
-	protected function sendEmail($input) {
290
-		$user = $this->findUserByIdOrMail($input);
291
-		$email = $user->getEMailAddress();
292
-
293
-		if (empty($email)) {
294
-			throw new \Exception(
295
-				$this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
296
-			);
297
-		}
298
-
299
-		// Generate the token. It is stored encrypted in the database with the
300
-		// secret being the users' email address appended with the system secret.
301
-		// This makes the token automatically invalidate once the user changes
302
-		// their email address.
303
-		$token = $this->secureRandom->generate(
304
-			21,
305
-			ISecureRandom::CHAR_DIGITS.
306
-			ISecureRandom::CHAR_LOWER.
307
-			ISecureRandom::CHAR_UPPER
308
-		);
309
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
310
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
311
-		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
312
-
313
-		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
314
-
315
-		$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
316
-			'link' => $link,
317
-		]);
318
-
319
-		$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
320
-		$emailTemplate->addHeader();
321
-		$emailTemplate->addHeading($this->l10n->t('Password reset'));
322
-
323
-		$emailTemplate->addBodyText(
324
-			htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
325
-			$this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
326
-		);
327
-
328
-		$emailTemplate->addBodyButton(
329
-			htmlspecialchars($this->l10n->t('Reset your password')),
330
-			$link,
331
-			false
332
-		);
333
-		$emailTemplate->addFooter();
334
-
335
-		try {
336
-			$message = $this->mailer->createMessage();
337
-			$message->setTo([$email => $user->getUID()]);
338
-			$message->setFrom([$this->from => $this->defaults->getName()]);
339
-			$message->useTemplate($emailTemplate);
340
-			$this->mailer->send($message);
341
-		} catch (\Exception $e) {
342
-			throw new \Exception($this->l10n->t(
343
-				'Couldn\'t send reset email. Please contact your administrator.'
344
-			));
345
-		}
346
-	}
347
-
348
-	/**
349
-	 * @param string $input
350
-	 * @return IUser
351
-	 * @throws \InvalidArgumentException
352
-	 */
353
-	protected function findUserByIdOrMail($input) {
354
-		$user = $this->userManager->get($input);
355
-		if ($user instanceof IUser) {
356
-			if (!$user->isEnabled()) {
357
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
358
-			}
359
-
360
-			return $user;
361
-		}
362
-		$users = $this->userManager->getByEmail($input);
363
-		if (count($users) === 1) {
364
-			$user = $users[0];
365
-			if (!$user->isEnabled()) {
366
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
367
-			}
368
-
369
-			return $user;
370
-		}
371
-
372
-		throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
373
-	}
59
+    /** @var IURLGenerator */
60
+    protected $urlGenerator;
61
+    /** @var IUserManager */
62
+    protected $userManager;
63
+    /** @var Defaults */
64
+    protected $defaults;
65
+    /** @var IL10N */
66
+    protected $l10n;
67
+    /** @var string */
68
+    protected $from;
69
+    /** @var IManager */
70
+    protected $encryptionManager;
71
+    /** @var IConfig */
72
+    protected $config;
73
+    /** @var ISecureRandom */
74
+    protected $secureRandom;
75
+    /** @var IMailer */
76
+    protected $mailer;
77
+    /** @var ITimeFactory */
78
+    protected $timeFactory;
79
+    /** @var ICrypto */
80
+    protected $crypto;
81
+
82
+    /**
83
+     * @param string $appName
84
+     * @param IRequest $request
85
+     * @param IURLGenerator $urlGenerator
86
+     * @param IUserManager $userManager
87
+     * @param Defaults $defaults
88
+     * @param IL10N $l10n
89
+     * @param IConfig $config
90
+     * @param ISecureRandom $secureRandom
91
+     * @param string $defaultMailAddress
92
+     * @param IManager $encryptionManager
93
+     * @param IMailer $mailer
94
+     * @param ITimeFactory $timeFactory
95
+     * @param ICrypto $crypto
96
+     */
97
+    public function __construct($appName,
98
+                                IRequest $request,
99
+                                IURLGenerator $urlGenerator,
100
+                                IUserManager $userManager,
101
+                                Defaults $defaults,
102
+                                IL10N $l10n,
103
+                                IConfig $config,
104
+                                ISecureRandom $secureRandom,
105
+                                $defaultMailAddress,
106
+                                IManager $encryptionManager,
107
+                                IMailer $mailer,
108
+                                ITimeFactory $timeFactory,
109
+                                ICrypto $crypto) {
110
+        parent::__construct($appName, $request);
111
+        $this->urlGenerator = $urlGenerator;
112
+        $this->userManager = $userManager;
113
+        $this->defaults = $defaults;
114
+        $this->l10n = $l10n;
115
+        $this->secureRandom = $secureRandom;
116
+        $this->from = $defaultMailAddress;
117
+        $this->encryptionManager = $encryptionManager;
118
+        $this->config = $config;
119
+        $this->mailer = $mailer;
120
+        $this->timeFactory = $timeFactory;
121
+        $this->crypto = $crypto;
122
+    }
123
+
124
+    /**
125
+     * Someone wants to reset their password:
126
+     *
127
+     * @PublicPage
128
+     * @NoCSRFRequired
129
+     *
130
+     * @param string $token
131
+     * @param string $userId
132
+     * @return TemplateResponse
133
+     */
134
+    public function resetform($token, $userId) {
135
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
136
+            return new TemplateResponse('core', 'error', [
137
+                    'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
138
+                ],
139
+                'guest'
140
+            );
141
+        }
142
+
143
+        try {
144
+            $this->checkPasswordResetToken($token, $userId);
145
+        } catch (\Exception $e) {
146
+            return new TemplateResponse(
147
+                'core', 'error', [
148
+                    "errors" => array(array("error" => $e->getMessage()))
149
+                ],
150
+                'guest'
151
+            );
152
+        }
153
+
154
+        return new TemplateResponse(
155
+            'core',
156
+            'lostpassword/resetpassword',
157
+            array(
158
+                'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
159
+            ),
160
+            'guest'
161
+        );
162
+    }
163
+
164
+    /**
165
+     * @param string $token
166
+     * @param string $userId
167
+     * @throws \Exception
168
+     */
169
+    protected function checkPasswordResetToken($token, $userId) {
170
+        $user = $this->userManager->get($userId);
171
+        if($user === null || !$user->isEnabled()) {
172
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
173
+        }
174
+
175
+        try {
176
+            $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
177
+            $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
178
+            $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
179
+        } catch (\Exception $e) {
180
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
181
+        }
182
+
183
+        $splittedToken = explode(':', $decryptedToken);
184
+        if(count($splittedToken) !== 2) {
185
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
186
+        }
187
+
188
+        if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
189
+            $user->getLastLogin() > $splittedToken[0]) {
190
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
191
+        }
192
+
193
+        if (!hash_equals($splittedToken[1], $token)) {
194
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
195
+        }
196
+    }
197
+
198
+    /**
199
+     * @param $message
200
+     * @param array $additional
201
+     * @return array
202
+     */
203
+    private function error($message, array $additional=array()) {
204
+        return array_merge(array('status' => 'error', 'msg' => $message), $additional);
205
+    }
206
+
207
+    /**
208
+     * @return array
209
+     */
210
+    private function success() {
211
+        return array('status'=>'success');
212
+    }
213
+
214
+    /**
215
+     * @PublicPage
216
+     * @BruteForceProtection(action=passwordResetEmail)
217
+     * @AnonRateThrottle(limit=10, period=300)
218
+     *
219
+     * @param string $user
220
+     * @return JSONResponse
221
+     */
222
+    public function email($user){
223
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
224
+            return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
225
+        }
226
+
227
+        \OCP\Util::emitHook(
228
+            '\OCA\Files_Sharing\API\Server2Server',
229
+            'preLoginNameUsedAsUserName',
230
+            ['uid' => &$user]
231
+        );
232
+
233
+        // FIXME: use HTTP error codes
234
+        try {
235
+            $this->sendEmail($user);
236
+        } catch (\Exception $e){
237
+            $response = new JSONResponse($this->error($e->getMessage()));
238
+            $response->throttle();
239
+            return $response;
240
+        }
241
+
242
+        $response = new JSONResponse($this->success());
243
+        $response->throttle();
244
+        return $response;
245
+    }
246
+
247
+    /**
248
+     * @PublicPage
249
+     * @param string $token
250
+     * @param string $userId
251
+     * @param string $password
252
+     * @param boolean $proceed
253
+     * @return array
254
+     */
255
+    public function setPassword($token, $userId, $password, $proceed) {
256
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
257
+            return $this->error($this->l10n->t('Password reset is disabled'));
258
+        }
259
+
260
+        if ($this->encryptionManager->isEnabled() && !$proceed) {
261
+            return $this->error('', array('encryption' => true));
262
+        }
263
+
264
+        try {
265
+            $this->checkPasswordResetToken($token, $userId);
266
+            $user = $this->userManager->get($userId);
267
+
268
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
269
+
270
+            if (!$user->setPassword($password)) {
271
+                throw new \Exception();
272
+            }
273
+
274
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
275
+
276
+            $this->config->deleteUserValue($userId, 'core', 'lostpassword');
277
+            @\OC::$server->getUserSession()->unsetMagicInCookie();
278
+        } catch (\Exception $e){
279
+            return $this->error($e->getMessage());
280
+        }
281
+
282
+        return $this->success();
283
+    }
284
+
285
+    /**
286
+     * @param string $input
287
+     * @throws \Exception
288
+     */
289
+    protected function sendEmail($input) {
290
+        $user = $this->findUserByIdOrMail($input);
291
+        $email = $user->getEMailAddress();
292
+
293
+        if (empty($email)) {
294
+            throw new \Exception(
295
+                $this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
296
+            );
297
+        }
298
+
299
+        // Generate the token. It is stored encrypted in the database with the
300
+        // secret being the users' email address appended with the system secret.
301
+        // This makes the token automatically invalidate once the user changes
302
+        // their email address.
303
+        $token = $this->secureRandom->generate(
304
+            21,
305
+            ISecureRandom::CHAR_DIGITS.
306
+            ISecureRandom::CHAR_LOWER.
307
+            ISecureRandom::CHAR_UPPER
308
+        );
309
+        $tokenValue = $this->timeFactory->getTime() .':'. $token;
310
+        $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
311
+        $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
312
+
313
+        $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
314
+
315
+        $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
316
+            'link' => $link,
317
+        ]);
318
+
319
+        $emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
320
+        $emailTemplate->addHeader();
321
+        $emailTemplate->addHeading($this->l10n->t('Password reset'));
322
+
323
+        $emailTemplate->addBodyText(
324
+            htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
325
+            $this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
326
+        );
327
+
328
+        $emailTemplate->addBodyButton(
329
+            htmlspecialchars($this->l10n->t('Reset your password')),
330
+            $link,
331
+            false
332
+        );
333
+        $emailTemplate->addFooter();
334
+
335
+        try {
336
+            $message = $this->mailer->createMessage();
337
+            $message->setTo([$email => $user->getUID()]);
338
+            $message->setFrom([$this->from => $this->defaults->getName()]);
339
+            $message->useTemplate($emailTemplate);
340
+            $this->mailer->send($message);
341
+        } catch (\Exception $e) {
342
+            throw new \Exception($this->l10n->t(
343
+                'Couldn\'t send reset email. Please contact your administrator.'
344
+            ));
345
+        }
346
+    }
347
+
348
+    /**
349
+     * @param string $input
350
+     * @return IUser
351
+     * @throws \InvalidArgumentException
352
+     */
353
+    protected function findUserByIdOrMail($input) {
354
+        $user = $this->userManager->get($input);
355
+        if ($user instanceof IUser) {
356
+            if (!$user->isEnabled()) {
357
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
358
+            }
359
+
360
+            return $user;
361
+        }
362
+        $users = $this->userManager->getByEmail($input);
363
+        if (count($users) === 1) {
364
+            $user = $users[0];
365
+            if (!$user->isEnabled()) {
366
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
367
+            }
368
+
369
+            return $user;
370
+        }
371
+
372
+        throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
373
+    }
374 374
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Service/DBConfigService.php 3 patches
Doc Comments   +16 added lines patch added patch discarded remove patch
@@ -89,6 +89,9 @@  discard block
 block discarded – undo
89 89
 		return $this->getMountsFromQuery($query);
90 90
 	}
91 91
 
92
+	/**
93
+	 * @param string $userId
94
+	 */
92 95
 	public function getMountsForUser($userId, $groupIds) {
93 96
 		$builder = $this->connection->getQueryBuilder();
94 97
 		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
@@ -125,6 +128,10 @@  discard block
 block discarded – undo
125 128
 		return $this->getMountsFromQuery($query);
126 129
 	}
127 130
 
131
+	/**
132
+	 * @param integer $type
133
+	 * @param string|null $value
134
+	 */
128 135
 	protected function getForQuery(IQueryBuilder $builder, $type, $value) {
129 136
 		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
130 137
 			->from('external_mounts', 'm')
@@ -332,6 +339,9 @@  discard block
 block discarded – undo
332 339
 		}
333 340
 	}
334 341
 
342
+	/**
343
+	 * @param integer $mountId
344
+	 */
335 345
 	public function addApplicable($mountId, $type, $value) {
336 346
 		$this->connection->insertIfNotExist('*PREFIX*external_applicable', [
337 347
 			'mount_id' => $mountId,
@@ -340,6 +350,9 @@  discard block
 block discarded – undo
340 350
 		], ['mount_id', 'type', 'value']);
341 351
 	}
342 352
 
353
+	/**
354
+	 * @param integer $mountId
355
+	 */
343 356
 	public function removeApplicable($mountId, $type, $value) {
344 357
 		$builder = $this->connection->getQueryBuilder();
345 358
 		$query = $builder->delete('external_applicable')
@@ -473,6 +486,9 @@  discard block
 block discarded – undo
473 486
 		return array_combine($keys, $values);
474 487
 	}
475 488
 
489
+	/**
490
+	 * @param string $value
491
+	 */
476 492
 	private function encryptValue($value) {
477 493
 		return $this->crypto->encrypt($value);
478 494
 	}
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
 	 */
179 179
 	public function getAdminMountsForMultiple($type, array $values) {
180 180
 		$builder = $this->connection->getQueryBuilder();
181
-		$params = array_map(function ($value) use ($builder) {
181
+		$params = array_map(function($value) use ($builder) {
182 182
 			return $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR);
183 183
 		}, $values);
184 184
 
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
 				'type' => $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)
232 232
 			]);
233 233
 		$query->execute();
234
-		return (int)$this->connection->lastInsertId('*PREFIX*external_mounts');
234
+		return (int) $this->connection->lastInsertId('*PREFIX*external_mounts');
235 235
 	}
236 236
 
237 237
 	/**
@@ -367,7 +367,7 @@  discard block
 block discarded – undo
367 367
 		}
368 368
 		$uniqueMounts = array_values($uniqueMounts);
369 369
 
370
-		$mountIds = array_map(function ($mount) {
370
+		$mountIds = array_map(function($mount) {
371 371
 			return $mount['mount_id'];
372 372
 		}, $uniqueMounts);
373 373
 		$mountIds = array_values(array_unique($mountIds));
@@ -376,9 +376,9 @@  discard block
 block discarded – undo
376 376
 		$config = $this->getConfigForMounts($mountIds);
377 377
 		$options = $this->getOptionsForMounts($mountIds);
378 378
 
379
-		return array_map(function ($mount, $applicable, $config, $options) {
380
-			$mount['type'] = (int)$mount['type'];
381
-			$mount['priority'] = (int)$mount['priority'];
379
+		return array_map(function($mount, $applicable, $config, $options) {
380
+			$mount['type'] = (int) $mount['type'];
381
+			$mount['priority'] = (int) $mount['priority'];
382 382
 			$mount['applicable'] = $applicable;
383 383
 			$mount['config'] = $config;
384 384
 			$mount['options'] = $options;
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
 		}
401 401
 		$builder = $this->connection->getQueryBuilder();
402 402
 		$fields[] = 'mount_id';
403
-		$placeHolders = array_map(function ($id) use ($builder) {
403
+		$placeHolders = array_map(function($id) use ($builder) {
404 404
 			return $builder->createPositionalParameter($id, IQueryBuilder::PARAM_INT);
405 405
 		}, $mountIds);
406 406
 		$query = $builder->select($fields)
@@ -414,7 +414,7 @@  discard block
 block discarded – undo
414 414
 		}
415 415
 		foreach ($rows as $row) {
416 416
 			if (isset($row['type'])) {
417
-				$row['type'] = (int)$row['type'];
417
+				$row['type'] = (int) $row['type'];
418 418
 			}
419 419
 			$result[$row['mount_id']][] = $row;
420 420
 		}
@@ -445,8 +445,8 @@  discard block
 block discarded – undo
445 445
 	public function getOptionsForMounts($mountIds) {
446 446
 		$mountOptions = $this->selectForMounts('external_options', ['key', 'value'], $mountIds);
447 447
 		$optionsMap = array_map([$this, 'createKeyValueMap'], $mountOptions);
448
-		return array_map(function (array $options) {
449
-			return array_map(function ($option) {
448
+		return array_map(function(array $options) {
449
+			return array_map(function($option) {
450 450
 				return json_decode($option);
451 451
 			}, $options);
452 452
 		}, $optionsMap);
@@ -457,16 +457,16 @@  discard block
 block discarded – undo
457 457
 	 * @return array ['key1' => $value1, ...]
458 458
 	 */
459 459
 	private function createKeyValueMap(array $keyValuePairs) {
460
-		$decryptedPairts = array_map(function ($pair) {
460
+		$decryptedPairts = array_map(function($pair) {
461 461
 			if ($pair['key'] === 'password') {
462 462
 				$pair['value'] = $this->decryptValue($pair['value']);
463 463
 			}
464 464
 			return $pair;
465 465
 		}, $keyValuePairs);
466
-		$keys = array_map(function ($pair) {
466
+		$keys = array_map(function($pair) {
467 467
 			return $pair['key'];
468 468
 		}, $decryptedPairts);
469
-		$values = array_map(function ($pair) {
469
+		$values = array_map(function($pair) {
470 470
 			return $pair['value'];
471 471
 		}, $decryptedPairts);
472 472
 
Please login to merge, or discard this patch.
Indentation   +456 added lines, -456 removed lines patch added patch discarded remove patch
@@ -32,460 +32,460 @@
 block discarded – undo
32 32
  * Stores the mount config in the database
33 33
  */
34 34
 class DBConfigService {
35
-	const MOUNT_TYPE_ADMIN = 1;
36
-	const MOUNT_TYPE_PERSONAl = 2;
37
-
38
-	const APPLICABLE_TYPE_GLOBAL = 1;
39
-	const APPLICABLE_TYPE_GROUP = 2;
40
-	const APPLICABLE_TYPE_USER = 3;
41
-
42
-	/**
43
-	 * @var IDBConnection
44
-	 */
45
-	private $connection;
46
-
47
-	/**
48
-	 * @var ICrypto
49
-	 */
50
-	private $crypto;
51
-
52
-	/**
53
-	 * DBConfigService constructor.
54
-	 *
55
-	 * @param IDBConnection $connection
56
-	 * @param ICrypto $crypto
57
-	 */
58
-	public function __construct(IDBConnection $connection, ICrypto $crypto) {
59
-		$this->connection = $connection;
60
-		$this->crypto = $crypto;
61
-	}
62
-
63
-	/**
64
-	 * @param int $mountId
65
-	 * @return array
66
-	 */
67
-	public function getMountById($mountId) {
68
-		$builder = $this->connection->getQueryBuilder();
69
-		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
70
-			->from('external_mounts', 'm')
71
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
72
-		$mounts = $this->getMountsFromQuery($query);
73
-		if (count($mounts) > 0) {
74
-			return $mounts[0];
75
-		} else {
76
-			return null;
77
-		}
78
-	}
79
-
80
-	/**
81
-	 * Get all configured mounts
82
-	 *
83
-	 * @return array
84
-	 */
85
-	public function getAllMounts() {
86
-		$builder = $this->connection->getQueryBuilder();
87
-		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
88
-			->from('external_mounts');
89
-		return $this->getMountsFromQuery($query);
90
-	}
91
-
92
-	public function getMountsForUser($userId, $groupIds) {
93
-		$builder = $this->connection->getQueryBuilder();
94
-		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
95
-			->from('external_mounts', 'm')
96
-			->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
97
-			->where($builder->expr()->orX(
98
-				$builder->expr()->andX( // global mounts
99
-					$builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GLOBAL, IQueryBuilder::PARAM_INT)),
100
-					$builder->expr()->isNull('a.value')
101
-				),
102
-				$builder->expr()->andX( // mounts for user
103
-					$builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_USER, IQueryBuilder::PARAM_INT)),
104
-					$builder->expr()->eq('a.value', $builder->createNamedParameter($userId))
105
-				),
106
-				$builder->expr()->andX( // mounts for group
107
-					$builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
108
-					$builder->expr()->in('a.value', $builder->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY))
109
-				)
110
-			));
111
-
112
-		return $this->getMountsFromQuery($query);
113
-	}
114
-
115
-	/**
116
-	 * Get admin defined mounts
117
-	 *
118
-	 * @return array
119
-	 * @suppress SqlInjectionChecker
120
-	 */
121
-	public function getAdminMounts() {
122
-		$builder = $this->connection->getQueryBuilder();
123
-		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
124
-			->from('external_mounts')
125
-			->where($builder->expr()->eq('type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
126
-		return $this->getMountsFromQuery($query);
127
-	}
128
-
129
-	protected function getForQuery(IQueryBuilder $builder, $type, $value) {
130
-		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
131
-			->from('external_mounts', 'm')
132
-			->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
133
-			->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
134
-
135
-		if (is_null($value)) {
136
-			$query = $query->andWhere($builder->expr()->isNull('a.value'));
137
-		} else {
138
-			$query = $query->andWhere($builder->expr()->eq('a.value', $builder->createNamedParameter($value)));
139
-		}
140
-
141
-		return $query;
142
-	}
143
-
144
-	/**
145
-	 * Get mounts by applicable
146
-	 *
147
-	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
148
-	 * @param string|null $value user_id, group_id or null for global mounts
149
-	 * @return array
150
-	 */
151
-	public function getMountsFor($type, $value) {
152
-		$builder = $this->connection->getQueryBuilder();
153
-		$query = $this->getForQuery($builder, $type, $value);
154
-
155
-		return $this->getMountsFromQuery($query);
156
-	}
157
-
158
-	/**
159
-	 * Get admin defined mounts by applicable
160
-	 *
161
-	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
162
-	 * @param string|null $value user_id, group_id or null for global mounts
163
-	 * @return array
164
-	 * @suppress SqlInjectionChecker
165
-	 */
166
-	public function getAdminMountsFor($type, $value) {
167
-		$builder = $this->connection->getQueryBuilder();
168
-		$query = $this->getForQuery($builder, $type, $value);
169
-		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
170
-
171
-		return $this->getMountsFromQuery($query);
172
-	}
173
-
174
-	/**
175
-	 * Get admin defined mounts for multiple applicable
176
-	 *
177
-	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
178
-	 * @param string[] $values user_ids or group_ids
179
-	 * @return array
180
-	 * @suppress SqlInjectionChecker
181
-	 */
182
-	public function getAdminMountsForMultiple($type, array $values) {
183
-		$builder = $this->connection->getQueryBuilder();
184
-		$params = array_map(function ($value) use ($builder) {
185
-			return $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR);
186
-		}, $values);
187
-
188
-		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
189
-			->from('external_mounts', 'm')
190
-			->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
191
-			->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
192
-			->andWhere($builder->expr()->in('a.value', $params));
193
-		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
194
-
195
-		return $this->getMountsFromQuery($query);
196
-	}
197
-
198
-	/**
199
-	 * Get user defined mounts by applicable
200
-	 *
201
-	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
202
-	 * @param string|null $value user_id, group_id or null for global mounts
203
-	 * @return array
204
-	 * @suppress SqlInjectionChecker
205
-	 */
206
-	public function getUserMountsFor($type, $value) {
207
-		$builder = $this->connection->getQueryBuilder();
208
-		$query = $this->getForQuery($builder, $type, $value);
209
-		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_PERSONAl, IQueryBuilder::PARAM_INT)));
210
-
211
-		return $this->getMountsFromQuery($query);
212
-	}
213
-
214
-	/**
215
-	 * Add a mount to the database
216
-	 *
217
-	 * @param string $mountPoint
218
-	 * @param string $storageBackend
219
-	 * @param string $authBackend
220
-	 * @param int $priority
221
-	 * @param int $type self::MOUNT_TYPE_ADMIN or self::MOUNT_TYPE_PERSONAL
222
-	 * @return int the id of the new mount
223
-	 */
224
-	public function addMount($mountPoint, $storageBackend, $authBackend, $priority, $type) {
225
-		if (!$priority) {
226
-			$priority = 100;
227
-		}
228
-		$builder = $this->connection->getQueryBuilder();
229
-		$query = $builder->insert('external_mounts')
230
-			->values([
231
-				'mount_point' => $builder->createNamedParameter($mountPoint, IQueryBuilder::PARAM_STR),
232
-				'storage_backend' => $builder->createNamedParameter($storageBackend, IQueryBuilder::PARAM_STR),
233
-				'auth_backend' => $builder->createNamedParameter($authBackend, IQueryBuilder::PARAM_STR),
234
-				'priority' => $builder->createNamedParameter($priority, IQueryBuilder::PARAM_INT),
235
-				'type' => $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)
236
-			]);
237
-		$query->execute();
238
-		return (int)$this->connection->lastInsertId('*PREFIX*external_mounts');
239
-	}
240
-
241
-	/**
242
-	 * Remove a mount from the database
243
-	 *
244
-	 * @param int $mountId
245
-	 */
246
-	public function removeMount($mountId) {
247
-		$builder = $this->connection->getQueryBuilder();
248
-		$query = $builder->delete('external_mounts')
249
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
250
-		$query->execute();
251
-
252
-		$query = $builder->delete('external_applicable')
253
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
254
-		$query->execute();
255
-
256
-		$query = $builder->delete('external_config')
257
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
258
-		$query->execute();
259
-
260
-		$query = $builder->delete('external_options')
261
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
262
-		$query->execute();
263
-	}
264
-
265
-	/**
266
-	 * @param int $mountId
267
-	 * @param string $newMountPoint
268
-	 */
269
-	public function setMountPoint($mountId, $newMountPoint) {
270
-		$builder = $this->connection->getQueryBuilder();
271
-
272
-		$query = $builder->update('external_mounts')
273
-			->set('mount_point', $builder->createNamedParameter($newMountPoint))
274
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
275
-
276
-		$query->execute();
277
-	}
278
-
279
-	/**
280
-	 * @param int $mountId
281
-	 * @param string $newAuthBackend
282
-	 */
283
-	public function setAuthBackend($mountId, $newAuthBackend) {
284
-		$builder = $this->connection->getQueryBuilder();
285
-
286
-		$query = $builder->update('external_mounts')
287
-			->set('auth_backend', $builder->createNamedParameter($newAuthBackend))
288
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
289
-
290
-		$query->execute();
291
-	}
292
-
293
-	/**
294
-	 * @param int $mountId
295
-	 * @param string $key
296
-	 * @param string $value
297
-	 */
298
-	public function setConfig($mountId, $key, $value) {
299
-		if ($key === 'password') {
300
-			$value = $this->encryptValue($value);
301
-		}
302
-		$count = $this->connection->insertIfNotExist('*PREFIX*external_config', [
303
-			'mount_id' => $mountId,
304
-			'key' => $key,
305
-			'value' => $value
306
-		], ['mount_id', 'key']);
307
-		if ($count === 0) {
308
-			$builder = $this->connection->getQueryBuilder();
309
-			$query = $builder->update('external_config')
310
-				->set('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR))
311
-				->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
312
-				->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
313
-			$query->execute();
314
-		}
315
-	}
316
-
317
-	/**
318
-	 * @param int $mountId
319
-	 * @param string $key
320
-	 * @param string $value
321
-	 */
322
-	public function setOption($mountId, $key, $value) {
323
-
324
-		$count = $this->connection->insertIfNotExist('*PREFIX*external_options', [
325
-			'mount_id' => $mountId,
326
-			'key' => $key,
327
-			'value' => json_encode($value)
328
-		], ['mount_id', 'key']);
329
-		if ($count === 0) {
330
-			$builder = $this->connection->getQueryBuilder();
331
-			$query = $builder->update('external_options')
332
-				->set('value', $builder->createNamedParameter(json_encode($value), IQueryBuilder::PARAM_STR))
333
-				->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
334
-				->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
335
-			$query->execute();
336
-		}
337
-	}
338
-
339
-	public function addApplicable($mountId, $type, $value) {
340
-		$this->connection->insertIfNotExist('*PREFIX*external_applicable', [
341
-			'mount_id' => $mountId,
342
-			'type' => $type,
343
-			'value' => $value
344
-		], ['mount_id', 'type', 'value']);
345
-	}
346
-
347
-	public function removeApplicable($mountId, $type, $value) {
348
-		$builder = $this->connection->getQueryBuilder();
349
-		$query = $builder->delete('external_applicable')
350
-			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
351
-			->andWhere($builder->expr()->eq('type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
352
-
353
-		if (is_null($value)) {
354
-			$query = $query->andWhere($builder->expr()->isNull('value'));
355
-		} else {
356
-			$query = $query->andWhere($builder->expr()->eq('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR)));
357
-		}
358
-
359
-		$query->execute();
360
-	}
361
-
362
-	private function getMountsFromQuery(IQueryBuilder $query) {
363
-		$result = $query->execute();
364
-		$mounts = $result->fetchAll();
365
-		$uniqueMounts = [];
366
-		foreach ($mounts as $mount) {
367
-			$id = $mount['mount_id'];
368
-			if (!isset($uniqueMounts[$id])) {
369
-				$uniqueMounts[$id] = $mount;
370
-			}
371
-		}
372
-		$uniqueMounts = array_values($uniqueMounts);
373
-
374
-		$mountIds = array_map(function ($mount) {
375
-			return $mount['mount_id'];
376
-		}, $uniqueMounts);
377
-		$mountIds = array_values(array_unique($mountIds));
378
-
379
-		$applicable = $this->getApplicableForMounts($mountIds);
380
-		$config = $this->getConfigForMounts($mountIds);
381
-		$options = $this->getOptionsForMounts($mountIds);
382
-
383
-		return array_map(function ($mount, $applicable, $config, $options) {
384
-			$mount['type'] = (int)$mount['type'];
385
-			$mount['priority'] = (int)$mount['priority'];
386
-			$mount['applicable'] = $applicable;
387
-			$mount['config'] = $config;
388
-			$mount['options'] = $options;
389
-			return $mount;
390
-		}, $uniqueMounts, $applicable, $config, $options);
391
-	}
392
-
393
-	/**
394
-	 * Get mount options from a table grouped by mount id
395
-	 *
396
-	 * @param string $table
397
-	 * @param string[] $fields
398
-	 * @param int[] $mountIds
399
-	 * @return array [$mountId => [['field1' => $value1, ...], ...], ...]
400
-	 */
401
-	private function selectForMounts($table, array $fields, array $mountIds) {
402
-		if (count($mountIds) === 0) {
403
-			return [];
404
-		}
405
-		$builder = $this->connection->getQueryBuilder();
406
-		$fields[] = 'mount_id';
407
-		$placeHolders = array_map(function ($id) use ($builder) {
408
-			return $builder->createPositionalParameter($id, IQueryBuilder::PARAM_INT);
409
-		}, $mountIds);
410
-		$query = $builder->select($fields)
411
-			->from($table)
412
-			->where($builder->expr()->in('mount_id', $placeHolders));
413
-		$rows = $query->execute()->fetchAll();
414
-
415
-		$result = [];
416
-		foreach ($mountIds as $mountId) {
417
-			$result[$mountId] = [];
418
-		}
419
-		foreach ($rows as $row) {
420
-			if (isset($row['type'])) {
421
-				$row['type'] = (int)$row['type'];
422
-			}
423
-			$result[$row['mount_id']][] = $row;
424
-		}
425
-		return $result;
426
-	}
427
-
428
-	/**
429
-	 * @param int[] $mountIds
430
-	 * @return array [$id => [['type' => $type, 'value' => $value], ...], ...]
431
-	 */
432
-	public function getApplicableForMounts($mountIds) {
433
-		return $this->selectForMounts('external_applicable', ['type', 'value'], $mountIds);
434
-	}
435
-
436
-	/**
437
-	 * @param int[] $mountIds
438
-	 * @return array [$id => ['key1' => $value1, ...], ...]
439
-	 */
440
-	public function getConfigForMounts($mountIds) {
441
-		$mountConfigs = $this->selectForMounts('external_config', ['key', 'value'], $mountIds);
442
-		return array_map([$this, 'createKeyValueMap'], $mountConfigs);
443
-	}
444
-
445
-	/**
446
-	 * @param int[] $mountIds
447
-	 * @return array [$id => ['key1' => $value1, ...], ...]
448
-	 */
449
-	public function getOptionsForMounts($mountIds) {
450
-		$mountOptions = $this->selectForMounts('external_options', ['key', 'value'], $mountIds);
451
-		$optionsMap = array_map([$this, 'createKeyValueMap'], $mountOptions);
452
-		return array_map(function (array $options) {
453
-			return array_map(function ($option) {
454
-				return json_decode($option);
455
-			}, $options);
456
-		}, $optionsMap);
457
-	}
458
-
459
-	/**
460
-	 * @param array $keyValuePairs [['key'=>$key, 'value=>$value], ...]
461
-	 * @return array ['key1' => $value1, ...]
462
-	 */
463
-	private function createKeyValueMap(array $keyValuePairs) {
464
-		$decryptedPairts = array_map(function ($pair) {
465
-			if ($pair['key'] === 'password') {
466
-				$pair['value'] = $this->decryptValue($pair['value']);
467
-			}
468
-			return $pair;
469
-		}, $keyValuePairs);
470
-		$keys = array_map(function ($pair) {
471
-			return $pair['key'];
472
-		}, $decryptedPairts);
473
-		$values = array_map(function ($pair) {
474
-			return $pair['value'];
475
-		}, $decryptedPairts);
476
-
477
-		return array_combine($keys, $values);
478
-	}
479
-
480
-	private function encryptValue($value) {
481
-		return $this->crypto->encrypt($value);
482
-	}
483
-
484
-	private function decryptValue($value) {
485
-		try {
486
-			return $this->crypto->decrypt($value);
487
-		} catch (\Exception $e) {
488
-			return $value;
489
-		}
490
-	}
35
+    const MOUNT_TYPE_ADMIN = 1;
36
+    const MOUNT_TYPE_PERSONAl = 2;
37
+
38
+    const APPLICABLE_TYPE_GLOBAL = 1;
39
+    const APPLICABLE_TYPE_GROUP = 2;
40
+    const APPLICABLE_TYPE_USER = 3;
41
+
42
+    /**
43
+     * @var IDBConnection
44
+     */
45
+    private $connection;
46
+
47
+    /**
48
+     * @var ICrypto
49
+     */
50
+    private $crypto;
51
+
52
+    /**
53
+     * DBConfigService constructor.
54
+     *
55
+     * @param IDBConnection $connection
56
+     * @param ICrypto $crypto
57
+     */
58
+    public function __construct(IDBConnection $connection, ICrypto $crypto) {
59
+        $this->connection = $connection;
60
+        $this->crypto = $crypto;
61
+    }
62
+
63
+    /**
64
+     * @param int $mountId
65
+     * @return array
66
+     */
67
+    public function getMountById($mountId) {
68
+        $builder = $this->connection->getQueryBuilder();
69
+        $query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
70
+            ->from('external_mounts', 'm')
71
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
72
+        $mounts = $this->getMountsFromQuery($query);
73
+        if (count($mounts) > 0) {
74
+            return $mounts[0];
75
+        } else {
76
+            return null;
77
+        }
78
+    }
79
+
80
+    /**
81
+     * Get all configured mounts
82
+     *
83
+     * @return array
84
+     */
85
+    public function getAllMounts() {
86
+        $builder = $this->connection->getQueryBuilder();
87
+        $query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
88
+            ->from('external_mounts');
89
+        return $this->getMountsFromQuery($query);
90
+    }
91
+
92
+    public function getMountsForUser($userId, $groupIds) {
93
+        $builder = $this->connection->getQueryBuilder();
94
+        $query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
95
+            ->from('external_mounts', 'm')
96
+            ->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
97
+            ->where($builder->expr()->orX(
98
+                $builder->expr()->andX( // global mounts
99
+                    $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GLOBAL, IQueryBuilder::PARAM_INT)),
100
+                    $builder->expr()->isNull('a.value')
101
+                ),
102
+                $builder->expr()->andX( // mounts for user
103
+                    $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_USER, IQueryBuilder::PARAM_INT)),
104
+                    $builder->expr()->eq('a.value', $builder->createNamedParameter($userId))
105
+                ),
106
+                $builder->expr()->andX( // mounts for group
107
+                    $builder->expr()->eq('a.type', $builder->createNamedParameter(self::APPLICABLE_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
108
+                    $builder->expr()->in('a.value', $builder->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY))
109
+                )
110
+            ));
111
+
112
+        return $this->getMountsFromQuery($query);
113
+    }
114
+
115
+    /**
116
+     * Get admin defined mounts
117
+     *
118
+     * @return array
119
+     * @suppress SqlInjectionChecker
120
+     */
121
+    public function getAdminMounts() {
122
+        $builder = $this->connection->getQueryBuilder();
123
+        $query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
124
+            ->from('external_mounts')
125
+            ->where($builder->expr()->eq('type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
126
+        return $this->getMountsFromQuery($query);
127
+    }
128
+
129
+    protected function getForQuery(IQueryBuilder $builder, $type, $value) {
130
+        $query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
131
+            ->from('external_mounts', 'm')
132
+            ->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
133
+            ->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
134
+
135
+        if (is_null($value)) {
136
+            $query = $query->andWhere($builder->expr()->isNull('a.value'));
137
+        } else {
138
+            $query = $query->andWhere($builder->expr()->eq('a.value', $builder->createNamedParameter($value)));
139
+        }
140
+
141
+        return $query;
142
+    }
143
+
144
+    /**
145
+     * Get mounts by applicable
146
+     *
147
+     * @param int $type any of the self::APPLICABLE_TYPE_ constants
148
+     * @param string|null $value user_id, group_id or null for global mounts
149
+     * @return array
150
+     */
151
+    public function getMountsFor($type, $value) {
152
+        $builder = $this->connection->getQueryBuilder();
153
+        $query = $this->getForQuery($builder, $type, $value);
154
+
155
+        return $this->getMountsFromQuery($query);
156
+    }
157
+
158
+    /**
159
+     * Get admin defined mounts by applicable
160
+     *
161
+     * @param int $type any of the self::APPLICABLE_TYPE_ constants
162
+     * @param string|null $value user_id, group_id or null for global mounts
163
+     * @return array
164
+     * @suppress SqlInjectionChecker
165
+     */
166
+    public function getAdminMountsFor($type, $value) {
167
+        $builder = $this->connection->getQueryBuilder();
168
+        $query = $this->getForQuery($builder, $type, $value);
169
+        $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
170
+
171
+        return $this->getMountsFromQuery($query);
172
+    }
173
+
174
+    /**
175
+     * Get admin defined mounts for multiple applicable
176
+     *
177
+     * @param int $type any of the self::APPLICABLE_TYPE_ constants
178
+     * @param string[] $values user_ids or group_ids
179
+     * @return array
180
+     * @suppress SqlInjectionChecker
181
+     */
182
+    public function getAdminMountsForMultiple($type, array $values) {
183
+        $builder = $this->connection->getQueryBuilder();
184
+        $params = array_map(function ($value) use ($builder) {
185
+            return $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR);
186
+        }, $values);
187
+
188
+        $query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
189
+            ->from('external_mounts', 'm')
190
+            ->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
191
+            ->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
192
+            ->andWhere($builder->expr()->in('a.value', $params));
193
+        $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
194
+
195
+        return $this->getMountsFromQuery($query);
196
+    }
197
+
198
+    /**
199
+     * Get user defined mounts by applicable
200
+     *
201
+     * @param int $type any of the self::APPLICABLE_TYPE_ constants
202
+     * @param string|null $value user_id, group_id or null for global mounts
203
+     * @return array
204
+     * @suppress SqlInjectionChecker
205
+     */
206
+    public function getUserMountsFor($type, $value) {
207
+        $builder = $this->connection->getQueryBuilder();
208
+        $query = $this->getForQuery($builder, $type, $value);
209
+        $query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_PERSONAl, IQueryBuilder::PARAM_INT)));
210
+
211
+        return $this->getMountsFromQuery($query);
212
+    }
213
+
214
+    /**
215
+     * Add a mount to the database
216
+     *
217
+     * @param string $mountPoint
218
+     * @param string $storageBackend
219
+     * @param string $authBackend
220
+     * @param int $priority
221
+     * @param int $type self::MOUNT_TYPE_ADMIN or self::MOUNT_TYPE_PERSONAL
222
+     * @return int the id of the new mount
223
+     */
224
+    public function addMount($mountPoint, $storageBackend, $authBackend, $priority, $type) {
225
+        if (!$priority) {
226
+            $priority = 100;
227
+        }
228
+        $builder = $this->connection->getQueryBuilder();
229
+        $query = $builder->insert('external_mounts')
230
+            ->values([
231
+                'mount_point' => $builder->createNamedParameter($mountPoint, IQueryBuilder::PARAM_STR),
232
+                'storage_backend' => $builder->createNamedParameter($storageBackend, IQueryBuilder::PARAM_STR),
233
+                'auth_backend' => $builder->createNamedParameter($authBackend, IQueryBuilder::PARAM_STR),
234
+                'priority' => $builder->createNamedParameter($priority, IQueryBuilder::PARAM_INT),
235
+                'type' => $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)
236
+            ]);
237
+        $query->execute();
238
+        return (int)$this->connection->lastInsertId('*PREFIX*external_mounts');
239
+    }
240
+
241
+    /**
242
+     * Remove a mount from the database
243
+     *
244
+     * @param int $mountId
245
+     */
246
+    public function removeMount($mountId) {
247
+        $builder = $this->connection->getQueryBuilder();
248
+        $query = $builder->delete('external_mounts')
249
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
250
+        $query->execute();
251
+
252
+        $query = $builder->delete('external_applicable')
253
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
254
+        $query->execute();
255
+
256
+        $query = $builder->delete('external_config')
257
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
258
+        $query->execute();
259
+
260
+        $query = $builder->delete('external_options')
261
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
262
+        $query->execute();
263
+    }
264
+
265
+    /**
266
+     * @param int $mountId
267
+     * @param string $newMountPoint
268
+     */
269
+    public function setMountPoint($mountId, $newMountPoint) {
270
+        $builder = $this->connection->getQueryBuilder();
271
+
272
+        $query = $builder->update('external_mounts')
273
+            ->set('mount_point', $builder->createNamedParameter($newMountPoint))
274
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
275
+
276
+        $query->execute();
277
+    }
278
+
279
+    /**
280
+     * @param int $mountId
281
+     * @param string $newAuthBackend
282
+     */
283
+    public function setAuthBackend($mountId, $newAuthBackend) {
284
+        $builder = $this->connection->getQueryBuilder();
285
+
286
+        $query = $builder->update('external_mounts')
287
+            ->set('auth_backend', $builder->createNamedParameter($newAuthBackend))
288
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
289
+
290
+        $query->execute();
291
+    }
292
+
293
+    /**
294
+     * @param int $mountId
295
+     * @param string $key
296
+     * @param string $value
297
+     */
298
+    public function setConfig($mountId, $key, $value) {
299
+        if ($key === 'password') {
300
+            $value = $this->encryptValue($value);
301
+        }
302
+        $count = $this->connection->insertIfNotExist('*PREFIX*external_config', [
303
+            'mount_id' => $mountId,
304
+            'key' => $key,
305
+            'value' => $value
306
+        ], ['mount_id', 'key']);
307
+        if ($count === 0) {
308
+            $builder = $this->connection->getQueryBuilder();
309
+            $query = $builder->update('external_config')
310
+                ->set('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR))
311
+                ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
312
+                ->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
313
+            $query->execute();
314
+        }
315
+    }
316
+
317
+    /**
318
+     * @param int $mountId
319
+     * @param string $key
320
+     * @param string $value
321
+     */
322
+    public function setOption($mountId, $key, $value) {
323
+
324
+        $count = $this->connection->insertIfNotExist('*PREFIX*external_options', [
325
+            'mount_id' => $mountId,
326
+            'key' => $key,
327
+            'value' => json_encode($value)
328
+        ], ['mount_id', 'key']);
329
+        if ($count === 0) {
330
+            $builder = $this->connection->getQueryBuilder();
331
+            $query = $builder->update('external_options')
332
+                ->set('value', $builder->createNamedParameter(json_encode($value), IQueryBuilder::PARAM_STR))
333
+                ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
334
+                ->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
335
+            $query->execute();
336
+        }
337
+    }
338
+
339
+    public function addApplicable($mountId, $type, $value) {
340
+        $this->connection->insertIfNotExist('*PREFIX*external_applicable', [
341
+            'mount_id' => $mountId,
342
+            'type' => $type,
343
+            'value' => $value
344
+        ], ['mount_id', 'type', 'value']);
345
+    }
346
+
347
+    public function removeApplicable($mountId, $type, $value) {
348
+        $builder = $this->connection->getQueryBuilder();
349
+        $query = $builder->delete('external_applicable')
350
+            ->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
351
+            ->andWhere($builder->expr()->eq('type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
352
+
353
+        if (is_null($value)) {
354
+            $query = $query->andWhere($builder->expr()->isNull('value'));
355
+        } else {
356
+            $query = $query->andWhere($builder->expr()->eq('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR)));
357
+        }
358
+
359
+        $query->execute();
360
+    }
361
+
362
+    private function getMountsFromQuery(IQueryBuilder $query) {
363
+        $result = $query->execute();
364
+        $mounts = $result->fetchAll();
365
+        $uniqueMounts = [];
366
+        foreach ($mounts as $mount) {
367
+            $id = $mount['mount_id'];
368
+            if (!isset($uniqueMounts[$id])) {
369
+                $uniqueMounts[$id] = $mount;
370
+            }
371
+        }
372
+        $uniqueMounts = array_values($uniqueMounts);
373
+
374
+        $mountIds = array_map(function ($mount) {
375
+            return $mount['mount_id'];
376
+        }, $uniqueMounts);
377
+        $mountIds = array_values(array_unique($mountIds));
378
+
379
+        $applicable = $this->getApplicableForMounts($mountIds);
380
+        $config = $this->getConfigForMounts($mountIds);
381
+        $options = $this->getOptionsForMounts($mountIds);
382
+
383
+        return array_map(function ($mount, $applicable, $config, $options) {
384
+            $mount['type'] = (int)$mount['type'];
385
+            $mount['priority'] = (int)$mount['priority'];
386
+            $mount['applicable'] = $applicable;
387
+            $mount['config'] = $config;
388
+            $mount['options'] = $options;
389
+            return $mount;
390
+        }, $uniqueMounts, $applicable, $config, $options);
391
+    }
392
+
393
+    /**
394
+     * Get mount options from a table grouped by mount id
395
+     *
396
+     * @param string $table
397
+     * @param string[] $fields
398
+     * @param int[] $mountIds
399
+     * @return array [$mountId => [['field1' => $value1, ...], ...], ...]
400
+     */
401
+    private function selectForMounts($table, array $fields, array $mountIds) {
402
+        if (count($mountIds) === 0) {
403
+            return [];
404
+        }
405
+        $builder = $this->connection->getQueryBuilder();
406
+        $fields[] = 'mount_id';
407
+        $placeHolders = array_map(function ($id) use ($builder) {
408
+            return $builder->createPositionalParameter($id, IQueryBuilder::PARAM_INT);
409
+        }, $mountIds);
410
+        $query = $builder->select($fields)
411
+            ->from($table)
412
+            ->where($builder->expr()->in('mount_id', $placeHolders));
413
+        $rows = $query->execute()->fetchAll();
414
+
415
+        $result = [];
416
+        foreach ($mountIds as $mountId) {
417
+            $result[$mountId] = [];
418
+        }
419
+        foreach ($rows as $row) {
420
+            if (isset($row['type'])) {
421
+                $row['type'] = (int)$row['type'];
422
+            }
423
+            $result[$row['mount_id']][] = $row;
424
+        }
425
+        return $result;
426
+    }
427
+
428
+    /**
429
+     * @param int[] $mountIds
430
+     * @return array [$id => [['type' => $type, 'value' => $value], ...], ...]
431
+     */
432
+    public function getApplicableForMounts($mountIds) {
433
+        return $this->selectForMounts('external_applicable', ['type', 'value'], $mountIds);
434
+    }
435
+
436
+    /**
437
+     * @param int[] $mountIds
438
+     * @return array [$id => ['key1' => $value1, ...], ...]
439
+     */
440
+    public function getConfigForMounts($mountIds) {
441
+        $mountConfigs = $this->selectForMounts('external_config', ['key', 'value'], $mountIds);
442
+        return array_map([$this, 'createKeyValueMap'], $mountConfigs);
443
+    }
444
+
445
+    /**
446
+     * @param int[] $mountIds
447
+     * @return array [$id => ['key1' => $value1, ...], ...]
448
+     */
449
+    public function getOptionsForMounts($mountIds) {
450
+        $mountOptions = $this->selectForMounts('external_options', ['key', 'value'], $mountIds);
451
+        $optionsMap = array_map([$this, 'createKeyValueMap'], $mountOptions);
452
+        return array_map(function (array $options) {
453
+            return array_map(function ($option) {
454
+                return json_decode($option);
455
+            }, $options);
456
+        }, $optionsMap);
457
+    }
458
+
459
+    /**
460
+     * @param array $keyValuePairs [['key'=>$key, 'value=>$value], ...]
461
+     * @return array ['key1' => $value1, ...]
462
+     */
463
+    private function createKeyValueMap(array $keyValuePairs) {
464
+        $decryptedPairts = array_map(function ($pair) {
465
+            if ($pair['key'] === 'password') {
466
+                $pair['value'] = $this->decryptValue($pair['value']);
467
+            }
468
+            return $pair;
469
+        }, $keyValuePairs);
470
+        $keys = array_map(function ($pair) {
471
+            return $pair['key'];
472
+        }, $decryptedPairts);
473
+        $values = array_map(function ($pair) {
474
+            return $pair['value'];
475
+        }, $decryptedPairts);
476
+
477
+        return array_combine($keys, $values);
478
+    }
479
+
480
+    private function encryptValue($value) {
481
+        return $this->crypto->encrypt($value);
482
+    }
483
+
484
+    private function decryptValue($value) {
485
+        try {
486
+            return $this->crypto->decrypt($value);
487
+        } catch (\Exception $e) {
488
+            return $value;
489
+        }
490
+    }
491 491
 }
Please login to merge, or discard this patch.