Passed
Push — master ( bbba7b...22edb6 )
by Blizzz
09:41 queued 12s
created
apps/user_ldap/lib/Mapping/AbstractMapping.php 1 patch
Indentation   +239 added lines, -239 removed lines patch added patch discarded remove patch
@@ -31,293 +31,293 @@
 block discarded – undo
31 31
  * @package OCA\User_LDAP\Mapping
32 32
  */
33 33
 abstract class AbstractMapping {
34
-	/**
35
-	 * @var \OCP\IDBConnection $dbc
36
-	 */
37
-	protected $dbc;
34
+    /**
35
+     * @var \OCP\IDBConnection $dbc
36
+     */
37
+    protected $dbc;
38 38
 
39
-	/**
40
-	 * returns the DB table name which holds the mappings
41
-	 * @return string
42
-	 */
43
-	abstract protected function getTableName();
39
+    /**
40
+     * returns the DB table name which holds the mappings
41
+     * @return string
42
+     */
43
+    abstract protected function getTableName();
44 44
 
45
-	/**
46
-	 * @param \OCP\IDBConnection $dbc
47
-	 */
48
-	public function __construct(\OCP\IDBConnection $dbc) {
49
-		$this->dbc = $dbc;
50
-	}
45
+    /**
46
+     * @param \OCP\IDBConnection $dbc
47
+     */
48
+    public function __construct(\OCP\IDBConnection $dbc) {
49
+        $this->dbc = $dbc;
50
+    }
51 51
 
52
-	/**
53
-	 * checks whether a provided string represents an existing table col
54
-	 * @param string $col
55
-	 * @return bool
56
-	 */
57
-	public function isColNameValid($col) {
58
-		switch($col) {
59
-			case 'ldap_dn':
60
-			case 'owncloud_name':
61
-			case 'directory_uuid':
62
-				return true;
63
-			default:
64
-				return false;
65
-		}
66
-	}
52
+    /**
53
+     * checks whether a provided string represents an existing table col
54
+     * @param string $col
55
+     * @return bool
56
+     */
57
+    public function isColNameValid($col) {
58
+        switch($col) {
59
+            case 'ldap_dn':
60
+            case 'owncloud_name':
61
+            case 'directory_uuid':
62
+                return true;
63
+            default:
64
+                return false;
65
+        }
66
+    }
67 67
 
68
-	/**
69
-	 * Gets the value of one column based on a provided value of another column
70
-	 * @param string $fetchCol
71
-	 * @param string $compareCol
72
-	 * @param string $search
73
-	 * @throws \Exception
74
-	 * @return string|false
75
-	 */
76
-	protected function getXbyY($fetchCol, $compareCol, $search) {
77
-		if(!$this->isColNameValid($fetchCol)) {
78
-			//this is used internally only, but we don't want to risk
79
-			//having SQL injection at all.
80
-			throw new \Exception('Invalid Column Name');
81
-		}
82
-		$query = $this->dbc->prepare('
68
+    /**
69
+     * Gets the value of one column based on a provided value of another column
70
+     * @param string $fetchCol
71
+     * @param string $compareCol
72
+     * @param string $search
73
+     * @throws \Exception
74
+     * @return string|false
75
+     */
76
+    protected function getXbyY($fetchCol, $compareCol, $search) {
77
+        if(!$this->isColNameValid($fetchCol)) {
78
+            //this is used internally only, but we don't want to risk
79
+            //having SQL injection at all.
80
+            throw new \Exception('Invalid Column Name');
81
+        }
82
+        $query = $this->dbc->prepare('
83 83
 			SELECT `' . $fetchCol . '`
84 84
 			FROM `'. $this->getTableName() .'`
85 85
 			WHERE `' . $compareCol . '` = ?
86 86
 		');
87 87
 
88
-		$res = $query->execute([$search]);
89
-		if($res !== false) {
90
-			return $query->fetchColumn();
91
-		}
88
+        $res = $query->execute([$search]);
89
+        if($res !== false) {
90
+            return $query->fetchColumn();
91
+        }
92 92
 
93
-		return false;
94
-	}
93
+        return false;
94
+    }
95 95
 
96
-	/**
97
-	 * Performs a DELETE or UPDATE query to the database.
98
-	 * @param \Doctrine\DBAL\Driver\Statement $query
99
-	 * @param array $parameters
100
-	 * @return bool true if at least one row was modified, false otherwise
101
-	 */
102
-	protected function modify($query, $parameters) {
103
-		$result = $query->execute($parameters);
104
-		return ($result === true && $query->rowCount() > 0);
105
-	}
96
+    /**
97
+     * Performs a DELETE or UPDATE query to the database.
98
+     * @param \Doctrine\DBAL\Driver\Statement $query
99
+     * @param array $parameters
100
+     * @return bool true if at least one row was modified, false otherwise
101
+     */
102
+    protected function modify($query, $parameters) {
103
+        $result = $query->execute($parameters);
104
+        return ($result === true && $query->rowCount() > 0);
105
+    }
106 106
 
107
-	/**
108
-	 * Gets the LDAP DN based on the provided name.
109
-	 * Replaces Access::ocname2dn
110
-	 * @param string $name
111
-	 * @return string|false
112
-	 */
113
-	public function getDNByName($name) {
114
-		return $this->getXbyY('ldap_dn', 'owncloud_name', $name);
115
-	}
107
+    /**
108
+     * Gets the LDAP DN based on the provided name.
109
+     * Replaces Access::ocname2dn
110
+     * @param string $name
111
+     * @return string|false
112
+     */
113
+    public function getDNByName($name) {
114
+        return $this->getXbyY('ldap_dn', 'owncloud_name', $name);
115
+    }
116 116
 
117
-	/**
118
-	 * Updates the DN based on the given UUID
119
-	 * @param string $fdn
120
-	 * @param string $uuid
121
-	 * @return bool
122
-	 */
123
-	public function setDNbyUUID($fdn, $uuid) {
124
-		$query = $this->dbc->prepare('
117
+    /**
118
+     * Updates the DN based on the given UUID
119
+     * @param string $fdn
120
+     * @param string $uuid
121
+     * @return bool
122
+     */
123
+    public function setDNbyUUID($fdn, $uuid) {
124
+        $query = $this->dbc->prepare('
125 125
 			UPDATE `' . $this->getTableName() . '`
126 126
 			SET `ldap_dn` = ?
127 127
 			WHERE `directory_uuid` = ?
128 128
 		');
129 129
 
130
-		return $this->modify($query, [$fdn, $uuid]);
131
-	}
130
+        return $this->modify($query, [$fdn, $uuid]);
131
+    }
132 132
 
133
-	/**
134
-	 * Updates the UUID based on the given DN
135
-	 *
136
-	 * required by Migration/UUIDFix
137
-	 *
138
-	 * @param $uuid
139
-	 * @param $fdn
140
-	 * @return bool
141
-	 */
142
-	public function setUUIDbyDN($uuid, $fdn) {
143
-		$query = $this->dbc->prepare('
133
+    /**
134
+     * Updates the UUID based on the given DN
135
+     *
136
+     * required by Migration/UUIDFix
137
+     *
138
+     * @param $uuid
139
+     * @param $fdn
140
+     * @return bool
141
+     */
142
+    public function setUUIDbyDN($uuid, $fdn) {
143
+        $query = $this->dbc->prepare('
144 144
 			UPDATE `' . $this->getTableName() . '`
145 145
 			SET `directory_uuid` = ?
146 146
 			WHERE `ldap_dn` = ?
147 147
 		');
148 148
 
149
-		return $this->modify($query, [$uuid, $fdn]);
150
-	}
149
+        return $this->modify($query, [$uuid, $fdn]);
150
+    }
151 151
 
152
-	/**
153
-	 * Gets the name based on the provided LDAP DN.
154
-	 * @param string $fdn
155
-	 * @return string|false
156
-	 */
157
-	public function getNameByDN($fdn) {
158
-		return $this->getXbyY('owncloud_name', 'ldap_dn', $fdn);
159
-	}
152
+    /**
153
+     * Gets the name based on the provided LDAP DN.
154
+     * @param string $fdn
155
+     * @return string|false
156
+     */
157
+    public function getNameByDN($fdn) {
158
+        return $this->getXbyY('owncloud_name', 'ldap_dn', $fdn);
159
+    }
160 160
 
161
-	/**
162
-	 * Searches mapped names by the giving string in the name column
163
-	 * @param string $search
164
-	 * @param string $prefixMatch
165
-	 * @param string $postfixMatch
166
-	 * @return string[]
167
-	 */
168
-	public function getNamesBySearch($search, $prefixMatch = "", $postfixMatch = "") {
169
-		$query = $this->dbc->prepare('
161
+    /**
162
+     * Searches mapped names by the giving string in the name column
163
+     * @param string $search
164
+     * @param string $prefixMatch
165
+     * @param string $postfixMatch
166
+     * @return string[]
167
+     */
168
+    public function getNamesBySearch($search, $prefixMatch = "", $postfixMatch = "") {
169
+        $query = $this->dbc->prepare('
170 170
 			SELECT `owncloud_name`
171 171
 			FROM `'. $this->getTableName() .'`
172 172
 			WHERE `owncloud_name` LIKE ?
173 173
 		');
174 174
 
175
-		$res = $query->execute([$prefixMatch.$this->dbc->escapeLikeParameter($search).$postfixMatch]);
176
-		$names = [];
177
-		if($res !== false) {
178
-			while($row = $query->fetch()) {
179
-				$names[] = $row['owncloud_name'];
180
-			}
181
-		}
182
-		return $names;
183
-	}
175
+        $res = $query->execute([$prefixMatch.$this->dbc->escapeLikeParameter($search).$postfixMatch]);
176
+        $names = [];
177
+        if($res !== false) {
178
+            while($row = $query->fetch()) {
179
+                $names[] = $row['owncloud_name'];
180
+            }
181
+        }
182
+        return $names;
183
+    }
184 184
 
185
-	/**
186
-	 * Gets the name based on the provided LDAP UUID.
187
-	 * @param string $uuid
188
-	 * @return string|false
189
-	 */
190
-	public function getNameByUUID($uuid) {
191
-		return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid);
192
-	}
185
+    /**
186
+     * Gets the name based on the provided LDAP UUID.
187
+     * @param string $uuid
188
+     * @return string|false
189
+     */
190
+    public function getNameByUUID($uuid) {
191
+        return $this->getXbyY('owncloud_name', 'directory_uuid', $uuid);
192
+    }
193 193
 
194
-	/**
195
-	 * Gets the UUID based on the provided LDAP DN
196
-	 * @param string $dn
197
-	 * @return false|string
198
-	 * @throws \Exception
199
-	 */
200
-	public function getUUIDByDN($dn) {
201
-		return $this->getXbyY('directory_uuid', 'ldap_dn', $dn);
202
-	}
194
+    /**
195
+     * Gets the UUID based on the provided LDAP DN
196
+     * @param string $dn
197
+     * @return false|string
198
+     * @throws \Exception
199
+     */
200
+    public function getUUIDByDN($dn) {
201
+        return $this->getXbyY('directory_uuid', 'ldap_dn', $dn);
202
+    }
203 203
 
204
-	/**
205
-	 * gets a piece of the mapping list
206
-	 * @param int $offset
207
-	 * @param int $limit
208
-	 * @return array
209
-	 */
210
-	public function getList($offset = null, $limit = null) {
211
-		$query = $this->dbc->prepare('
204
+    /**
205
+     * gets a piece of the mapping list
206
+     * @param int $offset
207
+     * @param int $limit
208
+     * @return array
209
+     */
210
+    public function getList($offset = null, $limit = null) {
211
+        $query = $this->dbc->prepare('
212 212
 			SELECT
213 213
 				`ldap_dn` AS `dn`,
214 214
 				`owncloud_name` AS `name`,
215 215
 				`directory_uuid` AS `uuid`
216 216
 			FROM `' . $this->getTableName() . '`',
217
-			$limit,
218
-			$offset
219
-		);
217
+            $limit,
218
+            $offset
219
+        );
220 220
 
221
-		$query->execute();
222
-		return $query->fetchAll();
223
-	}
221
+        $query->execute();
222
+        return $query->fetchAll();
223
+    }
224 224
 
225
-	/**
226
-	 * attempts to map the given entry
227
-	 * @param string $fdn fully distinguished name (from LDAP)
228
-	 * @param string $name
229
-	 * @param string $uuid a unique identifier as used in LDAP
230
-	 * @return bool
231
-	 */
232
-	public function map($fdn, $name, $uuid) {
233
-		if(mb_strlen($fdn) > 255) {
234
-			\OC::$server->getLogger()->error(
235
-				'Cannot map, because the DN exceeds 255 characters: {dn}',
236
-				[
237
-					'app' => 'user_ldap',
238
-					'dn' => $fdn,
239
-				]
240
-			);
241
-			return false;
242
-		}
225
+    /**
226
+     * attempts to map the given entry
227
+     * @param string $fdn fully distinguished name (from LDAP)
228
+     * @param string $name
229
+     * @param string $uuid a unique identifier as used in LDAP
230
+     * @return bool
231
+     */
232
+    public function map($fdn, $name, $uuid) {
233
+        if(mb_strlen($fdn) > 255) {
234
+            \OC::$server->getLogger()->error(
235
+                'Cannot map, because the DN exceeds 255 characters: {dn}',
236
+                [
237
+                    'app' => 'user_ldap',
238
+                    'dn' => $fdn,
239
+                ]
240
+            );
241
+            return false;
242
+        }
243 243
 
244
-		$row = [
245
-			'ldap_dn'        => $fdn,
246
-			'owncloud_name'  => $name,
247
-			'directory_uuid' => $uuid
248
-		];
244
+        $row = [
245
+            'ldap_dn'        => $fdn,
246
+            'owncloud_name'  => $name,
247
+            'directory_uuid' => $uuid
248
+        ];
249 249
 
250
-		try {
251
-			$result = $this->dbc->insertIfNotExist($this->getTableName(), $row);
252
-			// insertIfNotExist returns values as int
253
-			return (bool)$result;
254
-		} catch (\Exception $e) {
255
-			return false;
256
-		}
257
-	}
250
+        try {
251
+            $result = $this->dbc->insertIfNotExist($this->getTableName(), $row);
252
+            // insertIfNotExist returns values as int
253
+            return (bool)$result;
254
+        } catch (\Exception $e) {
255
+            return false;
256
+        }
257
+    }
258 258
 
259
-	/**
260
-	 * removes a mapping based on the owncloud_name of the entry
261
-	 * @param string $name
262
-	 * @return bool
263
-	 */
264
-	public function unmap($name) {
265
-		$query = $this->dbc->prepare('
259
+    /**
260
+     * removes a mapping based on the owncloud_name of the entry
261
+     * @param string $name
262
+     * @return bool
263
+     */
264
+    public function unmap($name) {
265
+        $query = $this->dbc->prepare('
266 266
 			DELETE FROM `'. $this->getTableName() .'`
267 267
 			WHERE `owncloud_name` = ?');
268 268
 
269
-		return $this->modify($query, [$name]);
270
-	}
269
+        return $this->modify($query, [$name]);
270
+    }
271 271
 
272
-	/**
273
-	 * Truncate's the mapping table
274
-	 * @return bool
275
-	 */
276
-	public function clear() {
277
-		$sql = $this->dbc
278
-			->getDatabasePlatform()
279
-			->getTruncateTableSQL('`' . $this->getTableName() . '`');
280
-		return $this->dbc->prepare($sql)->execute();
281
-	}
272
+    /**
273
+     * Truncate's the mapping table
274
+     * @return bool
275
+     */
276
+    public function clear() {
277
+        $sql = $this->dbc
278
+            ->getDatabasePlatform()
279
+            ->getTruncateTableSQL('`' . $this->getTableName() . '`');
280
+        return $this->dbc->prepare($sql)->execute();
281
+    }
282 282
 
283
-	/**
284
-	 * clears the mapping table one by one and executing a callback with
285
-	 * each row's id (=owncloud_name col)
286
-	 *
287
-	 * @param callable $preCallback
288
-	 * @param callable $postCallback
289
-	 * @return bool true on success, false when at least one row was not
290
-	 * deleted
291
-	 */
292
-	public function clearCb(Callable $preCallback, Callable $postCallback): bool {
293
-		$picker = $this->dbc->getQueryBuilder();
294
-		$picker->select('owncloud_name')
295
-			->from($this->getTableName());
296
-		$cursor = $picker->execute();
297
-		$result = true;
298
-		while($id = $cursor->fetchColumn(0)) {
299
-			$preCallback($id);
300
-			if($isUnmapped = $this->unmap($id)) {
301
-				$postCallback($id);
302
-			}
303
-			$result &= $isUnmapped;
304
-		}
305
-		$cursor->closeCursor();
306
-		return $result;
307
-	}
283
+    /**
284
+     * clears the mapping table one by one and executing a callback with
285
+     * each row's id (=owncloud_name col)
286
+     *
287
+     * @param callable $preCallback
288
+     * @param callable $postCallback
289
+     * @return bool true on success, false when at least one row was not
290
+     * deleted
291
+     */
292
+    public function clearCb(Callable $preCallback, Callable $postCallback): bool {
293
+        $picker = $this->dbc->getQueryBuilder();
294
+        $picker->select('owncloud_name')
295
+            ->from($this->getTableName());
296
+        $cursor = $picker->execute();
297
+        $result = true;
298
+        while($id = $cursor->fetchColumn(0)) {
299
+            $preCallback($id);
300
+            if($isUnmapped = $this->unmap($id)) {
301
+                $postCallback($id);
302
+            }
303
+            $result &= $isUnmapped;
304
+        }
305
+        $cursor->closeCursor();
306
+        return $result;
307
+    }
308 308
 
309
-	/**
310
-	 * returns the number of entries in the mappings table
311
-	 *
312
-	 * @return int
313
-	 */
314
-	public function count() {
315
-		$qb = $this->dbc->getQueryBuilder();
316
-		$query = $qb->select($qb->func()->count('ldap_dn'))
317
-			->from($this->getTableName());
318
-		$res = $query->execute();
319
-		$count = $res->fetchColumn();
320
-		$res->closeCursor();
321
-		return (int)$count;
322
-	}
309
+    /**
310
+     * returns the number of entries in the mappings table
311
+     *
312
+     * @return int
313
+     */
314
+    public function count() {
315
+        $qb = $this->dbc->getQueryBuilder();
316
+        $query = $qb->select($qb->func()->count('ldap_dn'))
317
+            ->from($this->getTableName());
318
+        $res = $query->execute();
319
+        $count = $res->fetchColumn();
320
+        $res->closeCursor();
321
+        return (int)$count;
322
+    }
323 323
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Access.php 2 patches
Indentation   +2028 added lines, -2028 removed lines patch added patch discarded remove patch
@@ -64,1779 +64,1779 @@  discard block
 block discarded – undo
64 64
  * @package OCA\User_LDAP
65 65
  */
66 66
 class Access extends LDAPUtility {
67
-	const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
68
-
69
-	/** @var \OCA\User_LDAP\Connection */
70
-	public $connection;
71
-	/** @var Manager */
72
-	public $userManager;
73
-	//never ever check this var directly, always use getPagedSearchResultState
74
-	protected $pagedSearchedSuccessful;
75
-
76
-	/**
77
-	 * @var string[] $cookies an array of returned Paged Result cookies
78
-	 */
79
-	protected $cookies = [];
80
-
81
-	/**
82
-	 * @var string $lastCookie the last cookie returned from a Paged Results
83
-	 * operation, defaults to an empty string
84
-	 */
85
-	protected $lastCookie = '';
86
-
87
-	/**
88
-	 * @var AbstractMapping $userMapper
89
-	 */
90
-	protected $userMapper;
91
-
92
-	/**
93
-	 * @var AbstractMapping $userMapper
94
-	 */
95
-	protected $groupMapper;
96
-
97
-	/**
98
-	 * @var \OCA\User_LDAP\Helper
99
-	 */
100
-	private $helper;
101
-	/** @var IConfig */
102
-	private $config;
103
-	/** @var IUserManager */
104
-	private $ncUserManager;
105
-
106
-	public function __construct(
107
-		Connection $connection,
108
-		ILDAPWrapper $ldap,
109
-		Manager $userManager,
110
-		Helper $helper,
111
-		IConfig $config,
112
-		IUserManager $ncUserManager
113
-	) {
114
-		parent::__construct($ldap);
115
-		$this->connection = $connection;
116
-		$this->userManager = $userManager;
117
-		$this->userManager->setLdapAccess($this);
118
-		$this->helper = $helper;
119
-		$this->config = $config;
120
-		$this->ncUserManager = $ncUserManager;
121
-	}
122
-
123
-	/**
124
-	 * sets the User Mapper
125
-	 * @param AbstractMapping $mapper
126
-	 */
127
-	public function setUserMapper(AbstractMapping $mapper) {
128
-		$this->userMapper = $mapper;
129
-	}
130
-
131
-	/**
132
-	 * returns the User Mapper
133
-	 * @throws \Exception
134
-	 * @return AbstractMapping
135
-	 */
136
-	public function getUserMapper() {
137
-		if(is_null($this->userMapper)) {
138
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
139
-		}
140
-		return $this->userMapper;
141
-	}
142
-
143
-	/**
144
-	 * sets the Group Mapper
145
-	 * @param AbstractMapping $mapper
146
-	 */
147
-	public function setGroupMapper(AbstractMapping $mapper) {
148
-		$this->groupMapper = $mapper;
149
-	}
150
-
151
-	/**
152
-	 * returns the Group Mapper
153
-	 * @throws \Exception
154
-	 * @return AbstractMapping
155
-	 */
156
-	public function getGroupMapper() {
157
-		if(is_null($this->groupMapper)) {
158
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
159
-		}
160
-		return $this->groupMapper;
161
-	}
162
-
163
-	/**
164
-	 * @return bool
165
-	 */
166
-	private function checkConnection() {
167
-		return ($this->connection instanceof Connection);
168
-	}
169
-
170
-	/**
171
-	 * returns the Connection instance
172
-	 * @return \OCA\User_LDAP\Connection
173
-	 */
174
-	public function getConnection() {
175
-		return $this->connection;
176
-	}
177
-
178
-	/**
179
-	 * reads a given attribute for an LDAP record identified by a DN
180
-	 *
181
-	 * @param string $dn the record in question
182
-	 * @param string $attr the attribute that shall be retrieved
183
-	 *        if empty, just check the record's existence
184
-	 * @param string $filter
185
-	 * @return array|false an array of values on success or an empty
186
-	 *          array if $attr is empty, false otherwise
187
-	 * @throws ServerNotAvailableException
188
-	 */
189
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
190
-		if(!$this->checkConnection()) {
191
-			\OCP\Util::writeLog('user_ldap',
192
-				'No LDAP Connector assigned, access impossible for readAttribute.',
193
-				ILogger::WARN);
194
-			return false;
195
-		}
196
-		$cr = $this->connection->getConnectionResource();
197
-		if(!$this->ldap->isResource($cr)) {
198
-			//LDAP not available
199
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
200
-			return false;
201
-		}
202
-		//Cancel possibly running Paged Results operation, otherwise we run in
203
-		//LDAP protocol errors
204
-		$this->abandonPagedSearch();
205
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
206
-		// but does not hurt either.
207
-		$pagingSize = (int)$this->connection->ldapPagingSize;
208
-		// 0 won't result in replies, small numbers may leave out groups
209
-		// (cf. #12306), 500 is default for paging and should work everywhere.
210
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
211
-		$attr = mb_strtolower($attr, 'UTF-8');
212
-		// the actual read attribute later may contain parameters on a ranged
213
-		// request, e.g. member;range=99-199. Depends on server reply.
214
-		$attrToRead = $attr;
215
-
216
-		$values = [];
217
-		$isRangeRequest = false;
218
-		do {
219
-			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
220
-			if(is_bool($result)) {
221
-				// when an exists request was run and it was successful, an empty
222
-				// array must be returned
223
-				return $result ? [] : false;
224
-			}
225
-
226
-			if (!$isRangeRequest) {
227
-				$values = $this->extractAttributeValuesFromResult($result, $attr);
228
-				if (!empty($values)) {
229
-					return $values;
230
-				}
231
-			}
232
-
233
-			$isRangeRequest = false;
234
-			$result = $this->extractRangeData($result, $attr);
235
-			if (!empty($result)) {
236
-				$normalizedResult = $this->extractAttributeValuesFromResult(
237
-					[ $attr => $result['values'] ],
238
-					$attr
239
-				);
240
-				$values = array_merge($values, $normalizedResult);
241
-
242
-				if($result['rangeHigh'] === '*') {
243
-					// when server replies with * as high range value, there are
244
-					// no more results left
245
-					return $values;
246
-				} else {
247
-					$low  = $result['rangeHigh'] + 1;
248
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
249
-					$isRangeRequest = true;
250
-				}
251
-			}
252
-		} while($isRangeRequest);
253
-
254
-		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, ILogger::DEBUG);
255
-		return false;
256
-	}
257
-
258
-	/**
259
-	 * Runs an read operation against LDAP
260
-	 *
261
-	 * @param resource $cr the LDAP connection
262
-	 * @param string $dn
263
-	 * @param string $attribute
264
-	 * @param string $filter
265
-	 * @param int $maxResults
266
-	 * @return array|bool false if there was any error, true if an exists check
267
-	 *                    was performed and the requested DN found, array with the
268
-	 *                    returned data on a successful usual operation
269
-	 * @throws ServerNotAvailableException
270
-	 */
271
-	public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
272
-		$this->initPagedSearch($filter, [$dn], [$attribute], $maxResults, 0);
273
-		$dn = $this->helper->DNasBaseParameter($dn);
274
-		$rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, [$attribute]);
275
-		if (!$this->ldap->isResource($rr)) {
276
-			if ($attribute !== '') {
277
-				//do not throw this message on userExists check, irritates
278
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, ILogger::DEBUG);
279
-			}
280
-			//in case an error occurs , e.g. object does not exist
281
-			return false;
282
-		}
283
-		if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
284
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', ILogger::DEBUG);
285
-			return true;
286
-		}
287
-		$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
288
-		if (!$this->ldap->isResource($er)) {
289
-			//did not match the filter, return false
290
-			return false;
291
-		}
292
-		//LDAP attributes are not case sensitive
293
-		$result = \OCP\Util::mb_array_change_key_case(
294
-			$this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
295
-
296
-		return $result;
297
-	}
298
-
299
-	/**
300
-	 * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
301
-	 * data if present.
302
-	 *
303
-	 * @param array $result from ILDAPWrapper::getAttributes()
304
-	 * @param string $attribute the attribute name that was read
305
-	 * @return string[]
306
-	 */
307
-	public function extractAttributeValuesFromResult($result, $attribute) {
308
-		$values = [];
309
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
310
-			$lowercaseAttribute = strtolower($attribute);
311
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
312
-				if($this->resemblesDN($attribute)) {
313
-					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
314
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
315
-					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
316
-				} else {
317
-					$values[] = $result[$attribute][$i];
318
-				}
319
-			}
320
-		}
321
-		return $values;
322
-	}
323
-
324
-	/**
325
-	 * Attempts to find ranged data in a getAttribute results and extracts the
326
-	 * returned values as well as information on the range and full attribute
327
-	 * name for further processing.
328
-	 *
329
-	 * @param array $result from ILDAPWrapper::getAttributes()
330
-	 * @param string $attribute the attribute name that was read. Without ";range=…"
331
-	 * @return array If a range was detected with keys 'values', 'attributeName',
332
-	 *               'attributeFull' and 'rangeHigh', otherwise empty.
333
-	 */
334
-	public function extractRangeData($result, $attribute) {
335
-		$keys = array_keys($result);
336
-		foreach($keys as $key) {
337
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
338
-				$queryData = explode(';', $key);
339
-				if(strpos($queryData[1], 'range=') === 0) {
340
-					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
341
-					$data = [
342
-						'values' => $result[$key],
343
-						'attributeName' => $queryData[0],
344
-						'attributeFull' => $key,
345
-						'rangeHigh' => $high,
346
-					];
347
-					return $data;
348
-				}
349
-			}
350
-		}
351
-		return [];
352
-	}
353
-
354
-	/**
355
-	 * Set password for an LDAP user identified by a DN
356
-	 *
357
-	 * @param string $userDN the user in question
358
-	 * @param string $password the new password
359
-	 * @return bool
360
-	 * @throws HintException
361
-	 * @throws \Exception
362
-	 */
363
-	public function setPassword($userDN, $password) {
364
-		if((int)$this->connection->turnOnPasswordChange !== 1) {
365
-			throw new \Exception('LDAP password changes are disabled.');
366
-		}
367
-		$cr = $this->connection->getConnectionResource();
368
-		if(!$this->ldap->isResource($cr)) {
369
-			//LDAP not available
370
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
371
-			return false;
372
-		}
373
-		try {
374
-			// try PASSWD extended operation first
375
-			return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
376
-						@$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
377
-		} catch(ConstraintViolationException $e) {
378
-			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
379
-		}
380
-	}
381
-
382
-	/**
383
-	 * checks whether the given attributes value is probably a DN
384
-	 * @param string $attr the attribute in question
385
-	 * @return boolean if so true, otherwise false
386
-	 */
387
-	private function resemblesDN($attr) {
388
-		$resemblingAttributes = [
389
-			'dn',
390
-			'uniquemember',
391
-			'member',
392
-			// memberOf is an "operational" attribute, without a definition in any RFC
393
-			'memberof'
394
-		];
395
-		return in_array($attr, $resemblingAttributes);
396
-	}
397
-
398
-	/**
399
-	 * checks whether the given string is probably a DN
400
-	 * @param string $string
401
-	 * @return boolean
402
-	 */
403
-	public function stringResemblesDN($string) {
404
-		$r = $this->ldap->explodeDN($string, 0);
405
-		// if exploding a DN succeeds and does not end up in
406
-		// an empty array except for $r[count] being 0.
407
-		return (is_array($r) && count($r) > 1);
408
-	}
409
-
410
-	/**
411
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
412
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
413
-	 * becomes dc=foobar,dc=server,dc=org
414
-	 * @param string $dn
415
-	 * @return string
416
-	 */
417
-	public function getDomainDNFromDN($dn) {
418
-		$allParts = $this->ldap->explodeDN($dn, 0);
419
-		if($allParts === false) {
420
-			//not a valid DN
421
-			return '';
422
-		}
423
-		$domainParts = [];
424
-		$dcFound = false;
425
-		foreach($allParts as $part) {
426
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
427
-				$dcFound = true;
428
-			}
429
-			if($dcFound) {
430
-				$domainParts[] = $part;
431
-			}
432
-		}
433
-		return implode(',', $domainParts);
434
-	}
435
-
436
-	/**
437
-	 * returns the LDAP DN for the given internal Nextcloud name of the group
438
-	 * @param string $name the Nextcloud name in question
439
-	 * @return string|false LDAP DN on success, otherwise false
440
-	 */
441
-	public function groupname2dn($name) {
442
-		return $this->groupMapper->getDNByName($name);
443
-	}
444
-
445
-	/**
446
-	 * returns the LDAP DN for the given internal Nextcloud name of the user
447
-	 * @param string $name the Nextcloud name in question
448
-	 * @return string|false with the LDAP DN on success, otherwise false
449
-	 */
450
-	public function username2dn($name) {
451
-		$fdn = $this->userMapper->getDNByName($name);
452
-
453
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
454
-		//server setups
455
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
456
-			return $fdn;
457
-		}
458
-
459
-		return false;
460
-	}
461
-
462
-	/**
463
-	 * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
464
-	 *
465
-	 * @param string $fdn the dn of the group object
466
-	 * @param string $ldapName optional, the display name of the object
467
-	 * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
468
-	 * @throws \Exception
469
-	 */
470
-	public function dn2groupname($fdn, $ldapName = null) {
471
-		//To avoid bypassing the base DN settings under certain circumstances
472
-		//with the group support, check whether the provided DN matches one of
473
-		//the given Bases
474
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
475
-			return false;
476
-		}
477
-
478
-		return $this->dn2ocname($fdn, $ldapName, false);
479
-	}
480
-
481
-	/**
482
-	 * accepts an array of group DNs and tests whether they match the user
483
-	 * filter by doing read operations against the group entries. Returns an
484
-	 * array of DNs that match the filter.
485
-	 *
486
-	 * @param string[] $groupDNs
487
-	 * @return string[]
488
-	 * @throws ServerNotAvailableException
489
-	 */
490
-	public function groupsMatchFilter($groupDNs) {
491
-		$validGroupDNs = [];
492
-		foreach($groupDNs as $dn) {
493
-			$cacheKey = 'groupsMatchFilter-'.$dn;
494
-			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
495
-			if(!is_null($groupMatchFilter)) {
496
-				if($groupMatchFilter) {
497
-					$validGroupDNs[] = $dn;
498
-				}
499
-				continue;
500
-			}
501
-
502
-			// Check the base DN first. If this is not met already, we don't
503
-			// need to ask the server at all.
504
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
505
-				$this->connection->writeToCache($cacheKey, false);
506
-				continue;
507
-			}
508
-
509
-			$result = $this->readAttribute($dn, '', $this->connection->ldapGroupFilter);
510
-			if(is_array($result)) {
511
-				$this->connection->writeToCache($cacheKey, true);
512
-				$validGroupDNs[] = $dn;
513
-			} else {
514
-				$this->connection->writeToCache($cacheKey, false);
515
-			}
516
-
517
-		}
518
-		return $validGroupDNs;
519
-	}
520
-
521
-	/**
522
-	 * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
523
-	 *
524
-	 * @param string $dn the dn of the user object
525
-	 * @param string $ldapName optional, the display name of the object
526
-	 * @return string|false with with the name to use in Nextcloud
527
-	 * @throws \Exception
528
-	 */
529
-	public function dn2username($fdn, $ldapName = null) {
530
-		//To avoid bypassing the base DN settings under certain circumstances
531
-		//with the group support, check whether the provided DN matches one of
532
-		//the given Bases
533
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
534
-			return false;
535
-		}
536
-
537
-		return $this->dn2ocname($fdn, $ldapName, true);
538
-	}
539
-
540
-	/**
541
-	 * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
542
-	 *
543
-	 * @param string $fdn the dn of the user object
544
-	 * @param string|null $ldapName optional, the display name of the object
545
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
546
-	 * @param bool|null $newlyMapped
547
-	 * @param array|null $record
548
-	 * @return false|string with with the name to use in Nextcloud
549
-	 * @throws \Exception
550
-	 */
551
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
552
-		$newlyMapped = false;
553
-		if($isUser) {
554
-			$mapper = $this->getUserMapper();
555
-			$nameAttribute = $this->connection->ldapUserDisplayName;
556
-			$filter = $this->connection->ldapUserFilter;
557
-		} else {
558
-			$mapper = $this->getGroupMapper();
559
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
560
-			$filter = $this->connection->ldapGroupFilter;
561
-		}
562
-
563
-		//let's try to retrieve the Nextcloud name from the mappings table
564
-		$ncName = $mapper->getNameByDN($fdn);
565
-		if(is_string($ncName)) {
566
-			return $ncName;
567
-		}
568
-
569
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
570
-		$uuid = $this->getUUID($fdn, $isUser, $record);
571
-		if(is_string($uuid)) {
572
-			$ncName = $mapper->getNameByUUID($uuid);
573
-			if(is_string($ncName)) {
574
-				$mapper->setDNbyUUID($fdn, $uuid);
575
-				return $ncName;
576
-			}
577
-		} else {
578
-			//If the UUID can't be detected something is foul.
579
-			\OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', ILogger::INFO);
580
-			return false;
581
-		}
582
-
583
-		if(is_null($ldapName)) {
584
-			$ldapName = $this->readAttribute($fdn, $nameAttribute, $filter);
585
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
586
-				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.' with filter '.$filter.'.', ILogger::INFO);
587
-				return false;
588
-			}
589
-			$ldapName = $ldapName[0];
590
-		}
591
-
592
-		if($isUser) {
593
-			$usernameAttribute = (string)$this->connection->ldapExpertUsernameAttr;
594
-			if ($usernameAttribute !== '') {
595
-				$username = $this->readAttribute($fdn, $usernameAttribute);
596
-				$username = $username[0];
597
-			} else {
598
-				$username = $uuid;
599
-			}
600
-			try {
601
-				$intName = $this->sanitizeUsername($username);
602
-			} catch (\InvalidArgumentException $e) {
603
-				\OC::$server->getLogger()->logException($e, [
604
-					'app' => 'user_ldap',
605
-					'level' => ILogger::WARN,
606
-				]);
607
-				// we don't attempt to set a username here. We can go for
608
-				// for an alternative 4 digit random number as we would append
609
-				// otherwise, however it's likely not enough space in bigger
610
-				// setups, and most importantly: this is not intended.
611
-				return false;
612
-			}
613
-		} else {
614
-			$intName = $ldapName;
615
-		}
616
-
617
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
618
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
619
-		//NOTE: mind, disabling cache affects only this instance! Using it
620
-		// outside of core user management will still cache the user as non-existing.
621
-		$originalTTL = $this->connection->ldapCacheTTL;
622
-		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
623
-		if( $intName !== ''
624
-			&& (($isUser && !$this->ncUserManager->userExists($intName))
625
-				|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))
626
-			)
627
-		) {
628
-			$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
629
-			$newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
630
-			if($newlyMapped) {
631
-				return $intName;
632
-			}
633
-		}
634
-
635
-		$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
636
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
637
-		if (is_string($altName)) {
638
-			if($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
639
-				$newlyMapped = true;
640
-				return $altName;
641
-			}
642
-		}
643
-
644
-		//if everything else did not help..
645
-		\OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', ILogger::INFO);
646
-		return false;
647
-	}
648
-
649
-	public function mapAndAnnounceIfApplicable(
650
-		AbstractMapping $mapper,
651
-		string $fdn,
652
-		string $name,
653
-		string $uuid,
654
-		bool $isUser
655
-	) :bool {
656
-		if($mapper->map($fdn, $name, $uuid)) {
657
-			if ($this->ncUserManager instanceof PublicEmitter && $isUser) {
658
-				$this->cacheUserExists($name);
659
-				$this->ncUserManager->emit('\OC\User', 'assignedUserId', [$name]);
660
-			} elseif (!$isUser) {
661
-				$this->cacheGroupExists($name);
662
-			}
663
-			return true;
664
-		}
665
-		return false;
666
-	}
667
-
668
-	/**
669
-	 * gives back the user names as they are used ownClod internally
670
-	 *
671
-	 * @param array $ldapUsers as returned by fetchList()
672
-	 * @return array an array with the user names to use in Nextcloud
673
-	 *
674
-	 * gives back the user names as they are used ownClod internally
675
-	 * @throws \Exception
676
-	 */
677
-	public function nextcloudUserNames($ldapUsers) {
678
-		return $this->ldap2NextcloudNames($ldapUsers, true);
679
-	}
680
-
681
-	/**
682
-	 * gives back the group names as they are used ownClod internally
683
-	 *
684
-	 * @param array $ldapGroups as returned by fetchList()
685
-	 * @return array an array with the group names to use in Nextcloud
686
-	 *
687
-	 * gives back the group names as they are used ownClod internally
688
-	 * @throws \Exception
689
-	 */
690
-	public function nextcloudGroupNames($ldapGroups) {
691
-		return $this->ldap2NextcloudNames($ldapGroups, false);
692
-	}
693
-
694
-	/**
695
-	 * @param array $ldapObjects as returned by fetchList()
696
-	 * @param bool $isUsers
697
-	 * @return array
698
-	 * @throws \Exception
699
-	 */
700
-	private function ldap2NextcloudNames($ldapObjects, $isUsers) {
701
-		if($isUsers) {
702
-			$nameAttribute = $this->connection->ldapUserDisplayName;
703
-			$sndAttribute  = $this->connection->ldapUserDisplayName2;
704
-		} else {
705
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
706
-		}
707
-		$nextcloudNames = [];
708
-
709
-		foreach($ldapObjects as $ldapObject) {
710
-			$nameByLDAP = null;
711
-			if(    isset($ldapObject[$nameAttribute])
712
-				&& is_array($ldapObject[$nameAttribute])
713
-				&& isset($ldapObject[$nameAttribute][0])
714
-			) {
715
-				// might be set, but not necessarily. if so, we use it.
716
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
717
-			}
718
-
719
-			$ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
720
-			if($ncName) {
721
-				$nextcloudNames[] = $ncName;
722
-				if($isUsers) {
723
-					$this->updateUserState($ncName);
724
-					//cache the user names so it does not need to be retrieved
725
-					//again later (e.g. sharing dialogue).
726
-					if(is_null($nameByLDAP)) {
727
-						continue;
728
-					}
729
-					$sndName = isset($ldapObject[$sndAttribute][0])
730
-						? $ldapObject[$sndAttribute][0] : '';
731
-					$this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
732
-				} else if($nameByLDAP !== null) {
733
-					$this->cacheGroupDisplayName($ncName, $nameByLDAP);
734
-				}
735
-			}
736
-		}
737
-		return $nextcloudNames;
738
-	}
739
-
740
-	/**
741
-	 * removes the deleted-flag of a user if it was set
742
-	 *
743
-	 * @param string $ncname
744
-	 * @throws \Exception
745
-	 */
746
-	public function updateUserState($ncname) {
747
-		$user = $this->userManager->get($ncname);
748
-		if($user instanceof OfflineUser) {
749
-			$user->unmark();
750
-		}
751
-	}
752
-
753
-	/**
754
-	 * caches the user display name
755
-	 * @param string $ocName the internal Nextcloud username
756
-	 * @param string|false $home the home directory path
757
-	 */
758
-	public function cacheUserHome($ocName, $home) {
759
-		$cacheKey = 'getHome'.$ocName;
760
-		$this->connection->writeToCache($cacheKey, $home);
761
-	}
762
-
763
-	/**
764
-	 * caches a user as existing
765
-	 * @param string $ocName the internal Nextcloud username
766
-	 */
767
-	public function cacheUserExists($ocName) {
768
-		$this->connection->writeToCache('userExists'.$ocName, true);
769
-	}
770
-
771
-	/**
772
-	 * caches a group as existing
773
-	 */
774
-	public function cacheGroupExists(string $gid): void {
775
-		$this->connection->writeToCache('groupExists'.$gid, true);
776
-	}
777
-
778
-	/**
779
-	 * caches the user display name
780
-	 *
781
-	 * @param string $ocName the internal Nextcloud username
782
-	 * @param string $displayName the display name
783
-	 * @param string $displayName2 the second display name
784
-	 * @throws \Exception
785
-	 */
786
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
787
-		$user = $this->userManager->get($ocName);
788
-		if($user === null) {
789
-			return;
790
-		}
791
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
792
-		$cacheKeyTrunk = 'getDisplayName';
793
-		$this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
794
-	}
795
-
796
-	public function cacheGroupDisplayName(string $ncName, string $displayName): void {
797
-		$cacheKey = 'group_getDisplayName' . $ncName;
798
-		$this->connection->writeToCache($cacheKey, $displayName);
799
-	}
800
-
801
-	/**
802
-	 * creates a unique name for internal Nextcloud use for users. Don't call it directly.
803
-	 * @param string $name the display name of the object
804
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
805
-	 *
806
-	 * Instead of using this method directly, call
807
-	 * createAltInternalOwnCloudName($name, true)
808
-	 */
809
-	private function _createAltInternalOwnCloudNameForUsers($name) {
810
-		$attempts = 0;
811
-		//while loop is just a precaution. If a name is not generated within
812
-		//20 attempts, something else is very wrong. Avoids infinite loop.
813
-		while($attempts < 20){
814
-			$altName = $name . '_' . rand(1000,9999);
815
-			if(!$this->ncUserManager->userExists($altName)) {
816
-				return $altName;
817
-			}
818
-			$attempts++;
819
-		}
820
-		return false;
821
-	}
822
-
823
-	/**
824
-	 * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
825
-	 * @param string $name the display name of the object
826
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
827
-	 *
828
-	 * Instead of using this method directly, call
829
-	 * createAltInternalOwnCloudName($name, false)
830
-	 *
831
-	 * Group names are also used as display names, so we do a sequential
832
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
833
-	 * "Developers"
834
-	 */
835
-	private function _createAltInternalOwnCloudNameForGroups($name) {
836
-		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
837
-		if(!$usedNames || count($usedNames) === 0) {
838
-			$lastNo = 1; //will become name_2
839
-		} else {
840
-			natsort($usedNames);
841
-			$lastName = array_pop($usedNames);
842
-			$lastNo = (int)substr($lastName, strrpos($lastName, '_') + 1);
843
-		}
844
-		$altName = $name.'_'. (string)($lastNo+1);
845
-		unset($usedNames);
846
-
847
-		$attempts = 1;
848
-		while($attempts < 21){
849
-			// Check to be really sure it is unique
850
-			// while loop is just a precaution. If a name is not generated within
851
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
852
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
853
-				return $altName;
854
-			}
855
-			$altName = $name . '_' . ($lastNo + $attempts);
856
-			$attempts++;
857
-		}
858
-		return false;
859
-	}
860
-
861
-	/**
862
-	 * creates a unique name for internal Nextcloud use.
863
-	 * @param string $name the display name of the object
864
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
865
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
866
-	 */
867
-	private function createAltInternalOwnCloudName($name, $isUser) {
868
-		$originalTTL = $this->connection->ldapCacheTTL;
869
-		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
870
-		if($isUser) {
871
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
872
-		} else {
873
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
874
-		}
875
-		$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
876
-
877
-		return $altName;
878
-	}
879
-
880
-	/**
881
-	 * fetches a list of users according to a provided loginName and utilizing
882
-	 * the login filter.
883
-	 *
884
-	 * @param string $loginName
885
-	 * @param array $attributes optional, list of attributes to read
886
-	 * @return array
887
-	 */
888
-	public function fetchUsersByLoginName($loginName, $attributes = ['dn']) {
889
-		$loginName = $this->escapeFilterPart($loginName);
890
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
891
-		return $this->fetchListOfUsers($filter, $attributes);
892
-	}
893
-
894
-	/**
895
-	 * counts the number of users according to a provided loginName and
896
-	 * utilizing the login filter.
897
-	 *
898
-	 * @param string $loginName
899
-	 * @return int
900
-	 */
901
-	public function countUsersByLoginName($loginName) {
902
-		$loginName = $this->escapeFilterPart($loginName);
903
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
904
-		return $this->countUsers($filter);
905
-	}
906
-
907
-	/**
908
-	 * @param string $filter
909
-	 * @param string|string[] $attr
910
-	 * @param int $limit
911
-	 * @param int $offset
912
-	 * @param bool $forceApplyAttributes
913
-	 * @return array
914
-	 * @throws \Exception
915
-	 */
916
-	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
917
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
918
-		$recordsToUpdate = $ldapRecords;
919
-		if(!$forceApplyAttributes) {
920
-			$isBackgroundJobModeAjax = $this->config
921
-					->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
922
-			$recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
923
-				$newlyMapped = false;
924
-				$uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
925
-				if(is_string($uid)) {
926
-					$this->cacheUserExists($uid);
927
-				}
928
-				return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
929
-			});
930
-		}
931
-		$this->batchApplyUserAttributes($recordsToUpdate);
932
-		return $this->fetchList($ldapRecords, $this->manyAttributes($attr));
933
-	}
934
-
935
-	/**
936
-	 * provided with an array of LDAP user records the method will fetch the
937
-	 * user object and requests it to process the freshly fetched attributes and
938
-	 * and their values
939
-	 *
940
-	 * @param array $ldapRecords
941
-	 * @throws \Exception
942
-	 */
943
-	public function batchApplyUserAttributes(array $ldapRecords){
944
-		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
945
-		foreach($ldapRecords as $userRecord) {
946
-			if(!isset($userRecord[$displayNameAttribute])) {
947
-				// displayName is obligatory
948
-				continue;
949
-			}
950
-			$ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
951
-			if($ocName === false) {
952
-				continue;
953
-			}
954
-			$this->updateUserState($ocName);
955
-			$user = $this->userManager->get($ocName);
956
-			if ($user !== null) {
957
-				$user->processAttributes($userRecord);
958
-			} else {
959
-				\OC::$server->getLogger()->debug(
960
-					"The ldap user manager returned null for $ocName",
961
-					['app'=>'user_ldap']
962
-				);
963
-			}
964
-		}
965
-	}
966
-
967
-	/**
968
-	 * @param string $filter
969
-	 * @param string|string[] $attr
970
-	 * @param int $limit
971
-	 * @param int $offset
972
-	 * @return array
973
-	 */
974
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
975
-		$groupRecords = $this->searchGroups($filter, $attr, $limit, $offset);
976
-		array_walk($groupRecords, function($record) {
977
-			$newlyMapped = false;
978
-			$gid = $this->dn2ocname($record['dn'][0], null, false, $newlyMapped, $record);
979
-			if(!$newlyMapped && is_string($gid)) {
980
-				$this->cacheGroupExists($gid);
981
-			}
982
-		});
983
-		return $this->fetchList($groupRecords, $this->manyAttributes($attr));
984
-	}
985
-
986
-	/**
987
-	 * @param array $list
988
-	 * @param bool $manyAttributes
989
-	 * @return array
990
-	 */
991
-	private function fetchList($list, $manyAttributes) {
992
-		if(is_array($list)) {
993
-			if($manyAttributes) {
994
-				return $list;
995
-			} else {
996
-				$list = array_reduce($list, function($carry, $item) {
997
-					$attribute = array_keys($item)[0];
998
-					$carry[] = $item[$attribute][0];
999
-					return $carry;
1000
-				}, []);
1001
-				return array_unique($list, SORT_LOCALE_STRING);
1002
-			}
1003
-		}
1004
-
1005
-		//error cause actually, maybe throw an exception in future.
1006
-		return [];
1007
-	}
1008
-
1009
-	/**
1010
-	 * executes an LDAP search, optimized for Users
1011
-	 *
1012
-	 * @param string $filter the LDAP filter for the search
1013
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1014
-	 * @param integer $limit
1015
-	 * @param integer $offset
1016
-	 * @return array with the search result
1017
-	 *
1018
-	 * Executes an LDAP search
1019
-	 * @throws ServerNotAvailableException
1020
-	 */
1021
-	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
1022
-		$result = [];
1023
-		foreach($this->connection->ldapBaseUsers as $base) {
1024
-			$result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1025
-		}
1026
-		return $result;
1027
-	}
1028
-
1029
-	/**
1030
-	 * @param string $filter
1031
-	 * @param string|string[] $attr
1032
-	 * @param int $limit
1033
-	 * @param int $offset
1034
-	 * @return false|int
1035
-	 * @throws ServerNotAvailableException
1036
-	 */
1037
-	public function countUsers($filter, $attr = ['dn'], $limit = null, $offset = null) {
1038
-		$result = false;
1039
-		foreach($this->connection->ldapBaseUsers as $base) {
1040
-			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1041
-			$result = is_int($count) ? (int)$result + $count : $result;
1042
-		}
1043
-		return $result;
1044
-	}
1045
-
1046
-	/**
1047
-	 * executes an LDAP search, optimized for Groups
1048
-	 *
1049
-	 * @param string $filter the LDAP filter for the search
1050
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1051
-	 * @param integer $limit
1052
-	 * @param integer $offset
1053
-	 * @return array with the search result
1054
-	 *
1055
-	 * Executes an LDAP search
1056
-	 * @throws ServerNotAvailableException
1057
-	 */
1058
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
1059
-		$result = [];
1060
-		foreach($this->connection->ldapBaseGroups as $base) {
1061
-			$result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1062
-		}
1063
-		return $result;
1064
-	}
1065
-
1066
-	/**
1067
-	 * returns the number of available groups
1068
-	 *
1069
-	 * @param string $filter the LDAP search filter
1070
-	 * @param string[] $attr optional
1071
-	 * @param int|null $limit
1072
-	 * @param int|null $offset
1073
-	 * @return int|bool
1074
-	 * @throws ServerNotAvailableException
1075
-	 */
1076
-	public function countGroups($filter, $attr = ['dn'], $limit = null, $offset = null) {
1077
-		$result = false;
1078
-		foreach($this->connection->ldapBaseGroups as $base) {
1079
-			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1080
-			$result = is_int($count) ? (int)$result + $count : $result;
1081
-		}
1082
-		return $result;
1083
-	}
1084
-
1085
-	/**
1086
-	 * returns the number of available objects on the base DN
1087
-	 *
1088
-	 * @param int|null $limit
1089
-	 * @param int|null $offset
1090
-	 * @return int|bool
1091
-	 * @throws ServerNotAvailableException
1092
-	 */
1093
-	public function countObjects($limit = null, $offset = null) {
1094
-		$result = false;
1095
-		foreach($this->connection->ldapBase as $base) {
1096
-			$count = $this->count('objectclass=*', [$base], ['dn'], $limit, $offset);
1097
-			$result = is_int($count) ? (int)$result + $count : $result;
1098
-		}
1099
-		return $result;
1100
-	}
1101
-
1102
-	/**
1103
-	 * Returns the LDAP handler
1104
-	 * @throws \OC\ServerNotAvailableException
1105
-	 */
1106
-
1107
-	/**
1108
-	 * @return mixed
1109
-	 * @throws \OC\ServerNotAvailableException
1110
-	 */
1111
-	private function invokeLDAPMethod() {
1112
-		$arguments = func_get_args();
1113
-		$command = array_shift($arguments);
1114
-		$cr = array_shift($arguments);
1115
-		if (!method_exists($this->ldap, $command)) {
1116
-			return null;
1117
-		}
1118
-		array_unshift($arguments, $cr);
1119
-		// php no longer supports call-time pass-by-reference
1120
-		// thus cannot support controlPagedResultResponse as the third argument
1121
-		// is a reference
1122
-		$doMethod = function () use ($command, &$arguments) {
1123
-			if ($command == 'controlPagedResultResponse') {
1124
-				throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1125
-			} else {
1126
-				return call_user_func_array([$this->ldap, $command], $arguments);
1127
-			}
1128
-		};
1129
-		try {
1130
-			$ret = $doMethod();
1131
-		} catch (ServerNotAvailableException $e) {
1132
-			/* Server connection lost, attempt to reestablish it
67
+    const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
68
+
69
+    /** @var \OCA\User_LDAP\Connection */
70
+    public $connection;
71
+    /** @var Manager */
72
+    public $userManager;
73
+    //never ever check this var directly, always use getPagedSearchResultState
74
+    protected $pagedSearchedSuccessful;
75
+
76
+    /**
77
+     * @var string[] $cookies an array of returned Paged Result cookies
78
+     */
79
+    protected $cookies = [];
80
+
81
+    /**
82
+     * @var string $lastCookie the last cookie returned from a Paged Results
83
+     * operation, defaults to an empty string
84
+     */
85
+    protected $lastCookie = '';
86
+
87
+    /**
88
+     * @var AbstractMapping $userMapper
89
+     */
90
+    protected $userMapper;
91
+
92
+    /**
93
+     * @var AbstractMapping $userMapper
94
+     */
95
+    protected $groupMapper;
96
+
97
+    /**
98
+     * @var \OCA\User_LDAP\Helper
99
+     */
100
+    private $helper;
101
+    /** @var IConfig */
102
+    private $config;
103
+    /** @var IUserManager */
104
+    private $ncUserManager;
105
+
106
+    public function __construct(
107
+        Connection $connection,
108
+        ILDAPWrapper $ldap,
109
+        Manager $userManager,
110
+        Helper $helper,
111
+        IConfig $config,
112
+        IUserManager $ncUserManager
113
+    ) {
114
+        parent::__construct($ldap);
115
+        $this->connection = $connection;
116
+        $this->userManager = $userManager;
117
+        $this->userManager->setLdapAccess($this);
118
+        $this->helper = $helper;
119
+        $this->config = $config;
120
+        $this->ncUserManager = $ncUserManager;
121
+    }
122
+
123
+    /**
124
+     * sets the User Mapper
125
+     * @param AbstractMapping $mapper
126
+     */
127
+    public function setUserMapper(AbstractMapping $mapper) {
128
+        $this->userMapper = $mapper;
129
+    }
130
+
131
+    /**
132
+     * returns the User Mapper
133
+     * @throws \Exception
134
+     * @return AbstractMapping
135
+     */
136
+    public function getUserMapper() {
137
+        if(is_null($this->userMapper)) {
138
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
139
+        }
140
+        return $this->userMapper;
141
+    }
142
+
143
+    /**
144
+     * sets the Group Mapper
145
+     * @param AbstractMapping $mapper
146
+     */
147
+    public function setGroupMapper(AbstractMapping $mapper) {
148
+        $this->groupMapper = $mapper;
149
+    }
150
+
151
+    /**
152
+     * returns the Group Mapper
153
+     * @throws \Exception
154
+     * @return AbstractMapping
155
+     */
156
+    public function getGroupMapper() {
157
+        if(is_null($this->groupMapper)) {
158
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
159
+        }
160
+        return $this->groupMapper;
161
+    }
162
+
163
+    /**
164
+     * @return bool
165
+     */
166
+    private function checkConnection() {
167
+        return ($this->connection instanceof Connection);
168
+    }
169
+
170
+    /**
171
+     * returns the Connection instance
172
+     * @return \OCA\User_LDAP\Connection
173
+     */
174
+    public function getConnection() {
175
+        return $this->connection;
176
+    }
177
+
178
+    /**
179
+     * reads a given attribute for an LDAP record identified by a DN
180
+     *
181
+     * @param string $dn the record in question
182
+     * @param string $attr the attribute that shall be retrieved
183
+     *        if empty, just check the record's existence
184
+     * @param string $filter
185
+     * @return array|false an array of values on success or an empty
186
+     *          array if $attr is empty, false otherwise
187
+     * @throws ServerNotAvailableException
188
+     */
189
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
190
+        if(!$this->checkConnection()) {
191
+            \OCP\Util::writeLog('user_ldap',
192
+                'No LDAP Connector assigned, access impossible for readAttribute.',
193
+                ILogger::WARN);
194
+            return false;
195
+        }
196
+        $cr = $this->connection->getConnectionResource();
197
+        if(!$this->ldap->isResource($cr)) {
198
+            //LDAP not available
199
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
200
+            return false;
201
+        }
202
+        //Cancel possibly running Paged Results operation, otherwise we run in
203
+        //LDAP protocol errors
204
+        $this->abandonPagedSearch();
205
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
206
+        // but does not hurt either.
207
+        $pagingSize = (int)$this->connection->ldapPagingSize;
208
+        // 0 won't result in replies, small numbers may leave out groups
209
+        // (cf. #12306), 500 is default for paging and should work everywhere.
210
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
211
+        $attr = mb_strtolower($attr, 'UTF-8');
212
+        // the actual read attribute later may contain parameters on a ranged
213
+        // request, e.g. member;range=99-199. Depends on server reply.
214
+        $attrToRead = $attr;
215
+
216
+        $values = [];
217
+        $isRangeRequest = false;
218
+        do {
219
+            $result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
220
+            if(is_bool($result)) {
221
+                // when an exists request was run and it was successful, an empty
222
+                // array must be returned
223
+                return $result ? [] : false;
224
+            }
225
+
226
+            if (!$isRangeRequest) {
227
+                $values = $this->extractAttributeValuesFromResult($result, $attr);
228
+                if (!empty($values)) {
229
+                    return $values;
230
+                }
231
+            }
232
+
233
+            $isRangeRequest = false;
234
+            $result = $this->extractRangeData($result, $attr);
235
+            if (!empty($result)) {
236
+                $normalizedResult = $this->extractAttributeValuesFromResult(
237
+                    [ $attr => $result['values'] ],
238
+                    $attr
239
+                );
240
+                $values = array_merge($values, $normalizedResult);
241
+
242
+                if($result['rangeHigh'] === '*') {
243
+                    // when server replies with * as high range value, there are
244
+                    // no more results left
245
+                    return $values;
246
+                } else {
247
+                    $low  = $result['rangeHigh'] + 1;
248
+                    $attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
249
+                    $isRangeRequest = true;
250
+                }
251
+            }
252
+        } while($isRangeRequest);
253
+
254
+        \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, ILogger::DEBUG);
255
+        return false;
256
+    }
257
+
258
+    /**
259
+     * Runs an read operation against LDAP
260
+     *
261
+     * @param resource $cr the LDAP connection
262
+     * @param string $dn
263
+     * @param string $attribute
264
+     * @param string $filter
265
+     * @param int $maxResults
266
+     * @return array|bool false if there was any error, true if an exists check
267
+     *                    was performed and the requested DN found, array with the
268
+     *                    returned data on a successful usual operation
269
+     * @throws ServerNotAvailableException
270
+     */
271
+    public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
272
+        $this->initPagedSearch($filter, [$dn], [$attribute], $maxResults, 0);
273
+        $dn = $this->helper->DNasBaseParameter($dn);
274
+        $rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, [$attribute]);
275
+        if (!$this->ldap->isResource($rr)) {
276
+            if ($attribute !== '') {
277
+                //do not throw this message on userExists check, irritates
278
+                \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, ILogger::DEBUG);
279
+            }
280
+            //in case an error occurs , e.g. object does not exist
281
+            return false;
282
+        }
283
+        if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
284
+            \OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', ILogger::DEBUG);
285
+            return true;
286
+        }
287
+        $er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
288
+        if (!$this->ldap->isResource($er)) {
289
+            //did not match the filter, return false
290
+            return false;
291
+        }
292
+        //LDAP attributes are not case sensitive
293
+        $result = \OCP\Util::mb_array_change_key_case(
294
+            $this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
295
+
296
+        return $result;
297
+    }
298
+
299
+    /**
300
+     * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
301
+     * data if present.
302
+     *
303
+     * @param array $result from ILDAPWrapper::getAttributes()
304
+     * @param string $attribute the attribute name that was read
305
+     * @return string[]
306
+     */
307
+    public function extractAttributeValuesFromResult($result, $attribute) {
308
+        $values = [];
309
+        if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
310
+            $lowercaseAttribute = strtolower($attribute);
311
+            for($i=0;$i<$result[$attribute]['count'];$i++) {
312
+                if($this->resemblesDN($attribute)) {
313
+                    $values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
314
+                } elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
315
+                    $values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
316
+                } else {
317
+                    $values[] = $result[$attribute][$i];
318
+                }
319
+            }
320
+        }
321
+        return $values;
322
+    }
323
+
324
+    /**
325
+     * Attempts to find ranged data in a getAttribute results and extracts the
326
+     * returned values as well as information on the range and full attribute
327
+     * name for further processing.
328
+     *
329
+     * @param array $result from ILDAPWrapper::getAttributes()
330
+     * @param string $attribute the attribute name that was read. Without ";range=…"
331
+     * @return array If a range was detected with keys 'values', 'attributeName',
332
+     *               'attributeFull' and 'rangeHigh', otherwise empty.
333
+     */
334
+    public function extractRangeData($result, $attribute) {
335
+        $keys = array_keys($result);
336
+        foreach($keys as $key) {
337
+            if($key !== $attribute && strpos($key, $attribute) === 0) {
338
+                $queryData = explode(';', $key);
339
+                if(strpos($queryData[1], 'range=') === 0) {
340
+                    $high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
341
+                    $data = [
342
+                        'values' => $result[$key],
343
+                        'attributeName' => $queryData[0],
344
+                        'attributeFull' => $key,
345
+                        'rangeHigh' => $high,
346
+                    ];
347
+                    return $data;
348
+                }
349
+            }
350
+        }
351
+        return [];
352
+    }
353
+
354
+    /**
355
+     * Set password for an LDAP user identified by a DN
356
+     *
357
+     * @param string $userDN the user in question
358
+     * @param string $password the new password
359
+     * @return bool
360
+     * @throws HintException
361
+     * @throws \Exception
362
+     */
363
+    public function setPassword($userDN, $password) {
364
+        if((int)$this->connection->turnOnPasswordChange !== 1) {
365
+            throw new \Exception('LDAP password changes are disabled.');
366
+        }
367
+        $cr = $this->connection->getConnectionResource();
368
+        if(!$this->ldap->isResource($cr)) {
369
+            //LDAP not available
370
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
371
+            return false;
372
+        }
373
+        try {
374
+            // try PASSWD extended operation first
375
+            return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
376
+                        @$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
377
+        } catch(ConstraintViolationException $e) {
378
+            throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
379
+        }
380
+    }
381
+
382
+    /**
383
+     * checks whether the given attributes value is probably a DN
384
+     * @param string $attr the attribute in question
385
+     * @return boolean if so true, otherwise false
386
+     */
387
+    private function resemblesDN($attr) {
388
+        $resemblingAttributes = [
389
+            'dn',
390
+            'uniquemember',
391
+            'member',
392
+            // memberOf is an "operational" attribute, without a definition in any RFC
393
+            'memberof'
394
+        ];
395
+        return in_array($attr, $resemblingAttributes);
396
+    }
397
+
398
+    /**
399
+     * checks whether the given string is probably a DN
400
+     * @param string $string
401
+     * @return boolean
402
+     */
403
+    public function stringResemblesDN($string) {
404
+        $r = $this->ldap->explodeDN($string, 0);
405
+        // if exploding a DN succeeds and does not end up in
406
+        // an empty array except for $r[count] being 0.
407
+        return (is_array($r) && count($r) > 1);
408
+    }
409
+
410
+    /**
411
+     * returns a DN-string that is cleaned from not domain parts, e.g.
412
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
413
+     * becomes dc=foobar,dc=server,dc=org
414
+     * @param string $dn
415
+     * @return string
416
+     */
417
+    public function getDomainDNFromDN($dn) {
418
+        $allParts = $this->ldap->explodeDN($dn, 0);
419
+        if($allParts === false) {
420
+            //not a valid DN
421
+            return '';
422
+        }
423
+        $domainParts = [];
424
+        $dcFound = false;
425
+        foreach($allParts as $part) {
426
+            if(!$dcFound && strpos($part, 'dc=') === 0) {
427
+                $dcFound = true;
428
+            }
429
+            if($dcFound) {
430
+                $domainParts[] = $part;
431
+            }
432
+        }
433
+        return implode(',', $domainParts);
434
+    }
435
+
436
+    /**
437
+     * returns the LDAP DN for the given internal Nextcloud name of the group
438
+     * @param string $name the Nextcloud name in question
439
+     * @return string|false LDAP DN on success, otherwise false
440
+     */
441
+    public function groupname2dn($name) {
442
+        return $this->groupMapper->getDNByName($name);
443
+    }
444
+
445
+    /**
446
+     * returns the LDAP DN for the given internal Nextcloud name of the user
447
+     * @param string $name the Nextcloud name in question
448
+     * @return string|false with the LDAP DN on success, otherwise false
449
+     */
450
+    public function username2dn($name) {
451
+        $fdn = $this->userMapper->getDNByName($name);
452
+
453
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
454
+        //server setups
455
+        if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
456
+            return $fdn;
457
+        }
458
+
459
+        return false;
460
+    }
461
+
462
+    /**
463
+     * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
464
+     *
465
+     * @param string $fdn the dn of the group object
466
+     * @param string $ldapName optional, the display name of the object
467
+     * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
468
+     * @throws \Exception
469
+     */
470
+    public function dn2groupname($fdn, $ldapName = null) {
471
+        //To avoid bypassing the base DN settings under certain circumstances
472
+        //with the group support, check whether the provided DN matches one of
473
+        //the given Bases
474
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
475
+            return false;
476
+        }
477
+
478
+        return $this->dn2ocname($fdn, $ldapName, false);
479
+    }
480
+
481
+    /**
482
+     * accepts an array of group DNs and tests whether they match the user
483
+     * filter by doing read operations against the group entries. Returns an
484
+     * array of DNs that match the filter.
485
+     *
486
+     * @param string[] $groupDNs
487
+     * @return string[]
488
+     * @throws ServerNotAvailableException
489
+     */
490
+    public function groupsMatchFilter($groupDNs) {
491
+        $validGroupDNs = [];
492
+        foreach($groupDNs as $dn) {
493
+            $cacheKey = 'groupsMatchFilter-'.$dn;
494
+            $groupMatchFilter = $this->connection->getFromCache($cacheKey);
495
+            if(!is_null($groupMatchFilter)) {
496
+                if($groupMatchFilter) {
497
+                    $validGroupDNs[] = $dn;
498
+                }
499
+                continue;
500
+            }
501
+
502
+            // Check the base DN first. If this is not met already, we don't
503
+            // need to ask the server at all.
504
+            if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
505
+                $this->connection->writeToCache($cacheKey, false);
506
+                continue;
507
+            }
508
+
509
+            $result = $this->readAttribute($dn, '', $this->connection->ldapGroupFilter);
510
+            if(is_array($result)) {
511
+                $this->connection->writeToCache($cacheKey, true);
512
+                $validGroupDNs[] = $dn;
513
+            } else {
514
+                $this->connection->writeToCache($cacheKey, false);
515
+            }
516
+
517
+        }
518
+        return $validGroupDNs;
519
+    }
520
+
521
+    /**
522
+     * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
523
+     *
524
+     * @param string $dn the dn of the user object
525
+     * @param string $ldapName optional, the display name of the object
526
+     * @return string|false with with the name to use in Nextcloud
527
+     * @throws \Exception
528
+     */
529
+    public function dn2username($fdn, $ldapName = null) {
530
+        //To avoid bypassing the base DN settings under certain circumstances
531
+        //with the group support, check whether the provided DN matches one of
532
+        //the given Bases
533
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
534
+            return false;
535
+        }
536
+
537
+        return $this->dn2ocname($fdn, $ldapName, true);
538
+    }
539
+
540
+    /**
541
+     * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
542
+     *
543
+     * @param string $fdn the dn of the user object
544
+     * @param string|null $ldapName optional, the display name of the object
545
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
546
+     * @param bool|null $newlyMapped
547
+     * @param array|null $record
548
+     * @return false|string with with the name to use in Nextcloud
549
+     * @throws \Exception
550
+     */
551
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
552
+        $newlyMapped = false;
553
+        if($isUser) {
554
+            $mapper = $this->getUserMapper();
555
+            $nameAttribute = $this->connection->ldapUserDisplayName;
556
+            $filter = $this->connection->ldapUserFilter;
557
+        } else {
558
+            $mapper = $this->getGroupMapper();
559
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
560
+            $filter = $this->connection->ldapGroupFilter;
561
+        }
562
+
563
+        //let's try to retrieve the Nextcloud name from the mappings table
564
+        $ncName = $mapper->getNameByDN($fdn);
565
+        if(is_string($ncName)) {
566
+            return $ncName;
567
+        }
568
+
569
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
570
+        $uuid = $this->getUUID($fdn, $isUser, $record);
571
+        if(is_string($uuid)) {
572
+            $ncName = $mapper->getNameByUUID($uuid);
573
+            if(is_string($ncName)) {
574
+                $mapper->setDNbyUUID($fdn, $uuid);
575
+                return $ncName;
576
+            }
577
+        } else {
578
+            //If the UUID can't be detected something is foul.
579
+            \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', ILogger::INFO);
580
+            return false;
581
+        }
582
+
583
+        if(is_null($ldapName)) {
584
+            $ldapName = $this->readAttribute($fdn, $nameAttribute, $filter);
585
+            if(!isset($ldapName[0]) && empty($ldapName[0])) {
586
+                \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.' with filter '.$filter.'.', ILogger::INFO);
587
+                return false;
588
+            }
589
+            $ldapName = $ldapName[0];
590
+        }
591
+
592
+        if($isUser) {
593
+            $usernameAttribute = (string)$this->connection->ldapExpertUsernameAttr;
594
+            if ($usernameAttribute !== '') {
595
+                $username = $this->readAttribute($fdn, $usernameAttribute);
596
+                $username = $username[0];
597
+            } else {
598
+                $username = $uuid;
599
+            }
600
+            try {
601
+                $intName = $this->sanitizeUsername($username);
602
+            } catch (\InvalidArgumentException $e) {
603
+                \OC::$server->getLogger()->logException($e, [
604
+                    'app' => 'user_ldap',
605
+                    'level' => ILogger::WARN,
606
+                ]);
607
+                // we don't attempt to set a username here. We can go for
608
+                // for an alternative 4 digit random number as we would append
609
+                // otherwise, however it's likely not enough space in bigger
610
+                // setups, and most importantly: this is not intended.
611
+                return false;
612
+            }
613
+        } else {
614
+            $intName = $ldapName;
615
+        }
616
+
617
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
618
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
619
+        //NOTE: mind, disabling cache affects only this instance! Using it
620
+        // outside of core user management will still cache the user as non-existing.
621
+        $originalTTL = $this->connection->ldapCacheTTL;
622
+        $this->connection->setConfiguration(['ldapCacheTTL' => 0]);
623
+        if( $intName !== ''
624
+            && (($isUser && !$this->ncUserManager->userExists($intName))
625
+                || (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))
626
+            )
627
+        ) {
628
+            $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
629
+            $newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
630
+            if($newlyMapped) {
631
+                return $intName;
632
+            }
633
+        }
634
+
635
+        $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
636
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
637
+        if (is_string($altName)) {
638
+            if($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
639
+                $newlyMapped = true;
640
+                return $altName;
641
+            }
642
+        }
643
+
644
+        //if everything else did not help..
645
+        \OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', ILogger::INFO);
646
+        return false;
647
+    }
648
+
649
+    public function mapAndAnnounceIfApplicable(
650
+        AbstractMapping $mapper,
651
+        string $fdn,
652
+        string $name,
653
+        string $uuid,
654
+        bool $isUser
655
+    ) :bool {
656
+        if($mapper->map($fdn, $name, $uuid)) {
657
+            if ($this->ncUserManager instanceof PublicEmitter && $isUser) {
658
+                $this->cacheUserExists($name);
659
+                $this->ncUserManager->emit('\OC\User', 'assignedUserId', [$name]);
660
+            } elseif (!$isUser) {
661
+                $this->cacheGroupExists($name);
662
+            }
663
+            return true;
664
+        }
665
+        return false;
666
+    }
667
+
668
+    /**
669
+     * gives back the user names as they are used ownClod internally
670
+     *
671
+     * @param array $ldapUsers as returned by fetchList()
672
+     * @return array an array with the user names to use in Nextcloud
673
+     *
674
+     * gives back the user names as they are used ownClod internally
675
+     * @throws \Exception
676
+     */
677
+    public function nextcloudUserNames($ldapUsers) {
678
+        return $this->ldap2NextcloudNames($ldapUsers, true);
679
+    }
680
+
681
+    /**
682
+     * gives back the group names as they are used ownClod internally
683
+     *
684
+     * @param array $ldapGroups as returned by fetchList()
685
+     * @return array an array with the group names to use in Nextcloud
686
+     *
687
+     * gives back the group names as they are used ownClod internally
688
+     * @throws \Exception
689
+     */
690
+    public function nextcloudGroupNames($ldapGroups) {
691
+        return $this->ldap2NextcloudNames($ldapGroups, false);
692
+    }
693
+
694
+    /**
695
+     * @param array $ldapObjects as returned by fetchList()
696
+     * @param bool $isUsers
697
+     * @return array
698
+     * @throws \Exception
699
+     */
700
+    private function ldap2NextcloudNames($ldapObjects, $isUsers) {
701
+        if($isUsers) {
702
+            $nameAttribute = $this->connection->ldapUserDisplayName;
703
+            $sndAttribute  = $this->connection->ldapUserDisplayName2;
704
+        } else {
705
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
706
+        }
707
+        $nextcloudNames = [];
708
+
709
+        foreach($ldapObjects as $ldapObject) {
710
+            $nameByLDAP = null;
711
+            if(    isset($ldapObject[$nameAttribute])
712
+                && is_array($ldapObject[$nameAttribute])
713
+                && isset($ldapObject[$nameAttribute][0])
714
+            ) {
715
+                // might be set, but not necessarily. if so, we use it.
716
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
717
+            }
718
+
719
+            $ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
720
+            if($ncName) {
721
+                $nextcloudNames[] = $ncName;
722
+                if($isUsers) {
723
+                    $this->updateUserState($ncName);
724
+                    //cache the user names so it does not need to be retrieved
725
+                    //again later (e.g. sharing dialogue).
726
+                    if(is_null($nameByLDAP)) {
727
+                        continue;
728
+                    }
729
+                    $sndName = isset($ldapObject[$sndAttribute][0])
730
+                        ? $ldapObject[$sndAttribute][0] : '';
731
+                    $this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
732
+                } else if($nameByLDAP !== null) {
733
+                    $this->cacheGroupDisplayName($ncName, $nameByLDAP);
734
+                }
735
+            }
736
+        }
737
+        return $nextcloudNames;
738
+    }
739
+
740
+    /**
741
+     * removes the deleted-flag of a user if it was set
742
+     *
743
+     * @param string $ncname
744
+     * @throws \Exception
745
+     */
746
+    public function updateUserState($ncname) {
747
+        $user = $this->userManager->get($ncname);
748
+        if($user instanceof OfflineUser) {
749
+            $user->unmark();
750
+        }
751
+    }
752
+
753
+    /**
754
+     * caches the user display name
755
+     * @param string $ocName the internal Nextcloud username
756
+     * @param string|false $home the home directory path
757
+     */
758
+    public function cacheUserHome($ocName, $home) {
759
+        $cacheKey = 'getHome'.$ocName;
760
+        $this->connection->writeToCache($cacheKey, $home);
761
+    }
762
+
763
+    /**
764
+     * caches a user as existing
765
+     * @param string $ocName the internal Nextcloud username
766
+     */
767
+    public function cacheUserExists($ocName) {
768
+        $this->connection->writeToCache('userExists'.$ocName, true);
769
+    }
770
+
771
+    /**
772
+     * caches a group as existing
773
+     */
774
+    public function cacheGroupExists(string $gid): void {
775
+        $this->connection->writeToCache('groupExists'.$gid, true);
776
+    }
777
+
778
+    /**
779
+     * caches the user display name
780
+     *
781
+     * @param string $ocName the internal Nextcloud username
782
+     * @param string $displayName the display name
783
+     * @param string $displayName2 the second display name
784
+     * @throws \Exception
785
+     */
786
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
787
+        $user = $this->userManager->get($ocName);
788
+        if($user === null) {
789
+            return;
790
+        }
791
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
792
+        $cacheKeyTrunk = 'getDisplayName';
793
+        $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
794
+    }
795
+
796
+    public function cacheGroupDisplayName(string $ncName, string $displayName): void {
797
+        $cacheKey = 'group_getDisplayName' . $ncName;
798
+        $this->connection->writeToCache($cacheKey, $displayName);
799
+    }
800
+
801
+    /**
802
+     * creates a unique name for internal Nextcloud use for users. Don't call it directly.
803
+     * @param string $name the display name of the object
804
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
805
+     *
806
+     * Instead of using this method directly, call
807
+     * createAltInternalOwnCloudName($name, true)
808
+     */
809
+    private function _createAltInternalOwnCloudNameForUsers($name) {
810
+        $attempts = 0;
811
+        //while loop is just a precaution. If a name is not generated within
812
+        //20 attempts, something else is very wrong. Avoids infinite loop.
813
+        while($attempts < 20){
814
+            $altName = $name . '_' . rand(1000,9999);
815
+            if(!$this->ncUserManager->userExists($altName)) {
816
+                return $altName;
817
+            }
818
+            $attempts++;
819
+        }
820
+        return false;
821
+    }
822
+
823
+    /**
824
+     * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
825
+     * @param string $name the display name of the object
826
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
827
+     *
828
+     * Instead of using this method directly, call
829
+     * createAltInternalOwnCloudName($name, false)
830
+     *
831
+     * Group names are also used as display names, so we do a sequential
832
+     * numbering, e.g. Developers_42 when there are 41 other groups called
833
+     * "Developers"
834
+     */
835
+    private function _createAltInternalOwnCloudNameForGroups($name) {
836
+        $usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
837
+        if(!$usedNames || count($usedNames) === 0) {
838
+            $lastNo = 1; //will become name_2
839
+        } else {
840
+            natsort($usedNames);
841
+            $lastName = array_pop($usedNames);
842
+            $lastNo = (int)substr($lastName, strrpos($lastName, '_') + 1);
843
+        }
844
+        $altName = $name.'_'. (string)($lastNo+1);
845
+        unset($usedNames);
846
+
847
+        $attempts = 1;
848
+        while($attempts < 21){
849
+            // Check to be really sure it is unique
850
+            // while loop is just a precaution. If a name is not generated within
851
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
852
+            if(!\OC::$server->getGroupManager()->groupExists($altName)) {
853
+                return $altName;
854
+            }
855
+            $altName = $name . '_' . ($lastNo + $attempts);
856
+            $attempts++;
857
+        }
858
+        return false;
859
+    }
860
+
861
+    /**
862
+     * creates a unique name for internal Nextcloud use.
863
+     * @param string $name the display name of the object
864
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
865
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
866
+     */
867
+    private function createAltInternalOwnCloudName($name, $isUser) {
868
+        $originalTTL = $this->connection->ldapCacheTTL;
869
+        $this->connection->setConfiguration(['ldapCacheTTL' => 0]);
870
+        if($isUser) {
871
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
872
+        } else {
873
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
874
+        }
875
+        $this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
876
+
877
+        return $altName;
878
+    }
879
+
880
+    /**
881
+     * fetches a list of users according to a provided loginName and utilizing
882
+     * the login filter.
883
+     *
884
+     * @param string $loginName
885
+     * @param array $attributes optional, list of attributes to read
886
+     * @return array
887
+     */
888
+    public function fetchUsersByLoginName($loginName, $attributes = ['dn']) {
889
+        $loginName = $this->escapeFilterPart($loginName);
890
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
891
+        return $this->fetchListOfUsers($filter, $attributes);
892
+    }
893
+
894
+    /**
895
+     * counts the number of users according to a provided loginName and
896
+     * utilizing the login filter.
897
+     *
898
+     * @param string $loginName
899
+     * @return int
900
+     */
901
+    public function countUsersByLoginName($loginName) {
902
+        $loginName = $this->escapeFilterPart($loginName);
903
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
904
+        return $this->countUsers($filter);
905
+    }
906
+
907
+    /**
908
+     * @param string $filter
909
+     * @param string|string[] $attr
910
+     * @param int $limit
911
+     * @param int $offset
912
+     * @param bool $forceApplyAttributes
913
+     * @return array
914
+     * @throws \Exception
915
+     */
916
+    public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
917
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
918
+        $recordsToUpdate = $ldapRecords;
919
+        if(!$forceApplyAttributes) {
920
+            $isBackgroundJobModeAjax = $this->config
921
+                    ->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
922
+            $recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
923
+                $newlyMapped = false;
924
+                $uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
925
+                if(is_string($uid)) {
926
+                    $this->cacheUserExists($uid);
927
+                }
928
+                return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
929
+            });
930
+        }
931
+        $this->batchApplyUserAttributes($recordsToUpdate);
932
+        return $this->fetchList($ldapRecords, $this->manyAttributes($attr));
933
+    }
934
+
935
+    /**
936
+     * provided with an array of LDAP user records the method will fetch the
937
+     * user object and requests it to process the freshly fetched attributes and
938
+     * and their values
939
+     *
940
+     * @param array $ldapRecords
941
+     * @throws \Exception
942
+     */
943
+    public function batchApplyUserAttributes(array $ldapRecords){
944
+        $displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
945
+        foreach($ldapRecords as $userRecord) {
946
+            if(!isset($userRecord[$displayNameAttribute])) {
947
+                // displayName is obligatory
948
+                continue;
949
+            }
950
+            $ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
951
+            if($ocName === false) {
952
+                continue;
953
+            }
954
+            $this->updateUserState($ocName);
955
+            $user = $this->userManager->get($ocName);
956
+            if ($user !== null) {
957
+                $user->processAttributes($userRecord);
958
+            } else {
959
+                \OC::$server->getLogger()->debug(
960
+                    "The ldap user manager returned null for $ocName",
961
+                    ['app'=>'user_ldap']
962
+                );
963
+            }
964
+        }
965
+    }
966
+
967
+    /**
968
+     * @param string $filter
969
+     * @param string|string[] $attr
970
+     * @param int $limit
971
+     * @param int $offset
972
+     * @return array
973
+     */
974
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
975
+        $groupRecords = $this->searchGroups($filter, $attr, $limit, $offset);
976
+        array_walk($groupRecords, function($record) {
977
+            $newlyMapped = false;
978
+            $gid = $this->dn2ocname($record['dn'][0], null, false, $newlyMapped, $record);
979
+            if(!$newlyMapped && is_string($gid)) {
980
+                $this->cacheGroupExists($gid);
981
+            }
982
+        });
983
+        return $this->fetchList($groupRecords, $this->manyAttributes($attr));
984
+    }
985
+
986
+    /**
987
+     * @param array $list
988
+     * @param bool $manyAttributes
989
+     * @return array
990
+     */
991
+    private function fetchList($list, $manyAttributes) {
992
+        if(is_array($list)) {
993
+            if($manyAttributes) {
994
+                return $list;
995
+            } else {
996
+                $list = array_reduce($list, function($carry, $item) {
997
+                    $attribute = array_keys($item)[0];
998
+                    $carry[] = $item[$attribute][0];
999
+                    return $carry;
1000
+                }, []);
1001
+                return array_unique($list, SORT_LOCALE_STRING);
1002
+            }
1003
+        }
1004
+
1005
+        //error cause actually, maybe throw an exception in future.
1006
+        return [];
1007
+    }
1008
+
1009
+    /**
1010
+     * executes an LDAP search, optimized for Users
1011
+     *
1012
+     * @param string $filter the LDAP filter for the search
1013
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1014
+     * @param integer $limit
1015
+     * @param integer $offset
1016
+     * @return array with the search result
1017
+     *
1018
+     * Executes an LDAP search
1019
+     * @throws ServerNotAvailableException
1020
+     */
1021
+    public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
1022
+        $result = [];
1023
+        foreach($this->connection->ldapBaseUsers as $base) {
1024
+            $result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1025
+        }
1026
+        return $result;
1027
+    }
1028
+
1029
+    /**
1030
+     * @param string $filter
1031
+     * @param string|string[] $attr
1032
+     * @param int $limit
1033
+     * @param int $offset
1034
+     * @return false|int
1035
+     * @throws ServerNotAvailableException
1036
+     */
1037
+    public function countUsers($filter, $attr = ['dn'], $limit = null, $offset = null) {
1038
+        $result = false;
1039
+        foreach($this->connection->ldapBaseUsers as $base) {
1040
+            $count = $this->count($filter, [$base], $attr, $limit, $offset);
1041
+            $result = is_int($count) ? (int)$result + $count : $result;
1042
+        }
1043
+        return $result;
1044
+    }
1045
+
1046
+    /**
1047
+     * executes an LDAP search, optimized for Groups
1048
+     *
1049
+     * @param string $filter the LDAP filter for the search
1050
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
1051
+     * @param integer $limit
1052
+     * @param integer $offset
1053
+     * @return array with the search result
1054
+     *
1055
+     * Executes an LDAP search
1056
+     * @throws ServerNotAvailableException
1057
+     */
1058
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
1059
+        $result = [];
1060
+        foreach($this->connection->ldapBaseGroups as $base) {
1061
+            $result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1062
+        }
1063
+        return $result;
1064
+    }
1065
+
1066
+    /**
1067
+     * returns the number of available groups
1068
+     *
1069
+     * @param string $filter the LDAP search filter
1070
+     * @param string[] $attr optional
1071
+     * @param int|null $limit
1072
+     * @param int|null $offset
1073
+     * @return int|bool
1074
+     * @throws ServerNotAvailableException
1075
+     */
1076
+    public function countGroups($filter, $attr = ['dn'], $limit = null, $offset = null) {
1077
+        $result = false;
1078
+        foreach($this->connection->ldapBaseGroups as $base) {
1079
+            $count = $this->count($filter, [$base], $attr, $limit, $offset);
1080
+            $result = is_int($count) ? (int)$result + $count : $result;
1081
+        }
1082
+        return $result;
1083
+    }
1084
+
1085
+    /**
1086
+     * returns the number of available objects on the base DN
1087
+     *
1088
+     * @param int|null $limit
1089
+     * @param int|null $offset
1090
+     * @return int|bool
1091
+     * @throws ServerNotAvailableException
1092
+     */
1093
+    public function countObjects($limit = null, $offset = null) {
1094
+        $result = false;
1095
+        foreach($this->connection->ldapBase as $base) {
1096
+            $count = $this->count('objectclass=*', [$base], ['dn'], $limit, $offset);
1097
+            $result = is_int($count) ? (int)$result + $count : $result;
1098
+        }
1099
+        return $result;
1100
+    }
1101
+
1102
+    /**
1103
+     * Returns the LDAP handler
1104
+     * @throws \OC\ServerNotAvailableException
1105
+     */
1106
+
1107
+    /**
1108
+     * @return mixed
1109
+     * @throws \OC\ServerNotAvailableException
1110
+     */
1111
+    private function invokeLDAPMethod() {
1112
+        $arguments = func_get_args();
1113
+        $command = array_shift($arguments);
1114
+        $cr = array_shift($arguments);
1115
+        if (!method_exists($this->ldap, $command)) {
1116
+            return null;
1117
+        }
1118
+        array_unshift($arguments, $cr);
1119
+        // php no longer supports call-time pass-by-reference
1120
+        // thus cannot support controlPagedResultResponse as the third argument
1121
+        // is a reference
1122
+        $doMethod = function () use ($command, &$arguments) {
1123
+            if ($command == 'controlPagedResultResponse') {
1124
+                throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1125
+            } else {
1126
+                return call_user_func_array([$this->ldap, $command], $arguments);
1127
+            }
1128
+        };
1129
+        try {
1130
+            $ret = $doMethod();
1131
+        } catch (ServerNotAvailableException $e) {
1132
+            /* Server connection lost, attempt to reestablish it
1133 1133
 			 * Maybe implement exponential backoff?
1134 1134
 			 * This was enough to get solr indexer working which has large delays between LDAP fetches.
1135 1135
 			 */
1136
-			\OCP\Util::writeLog('user_ldap', "Connection lost on $command, attempting to reestablish.", ILogger::DEBUG);
1137
-			$this->connection->resetConnectionResource();
1138
-			$cr = $this->connection->getConnectionResource();
1139
-
1140
-			if(!$this->ldap->isResource($cr)) {
1141
-				// Seems like we didn't find any resource.
1142
-				\OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", ILogger::DEBUG);
1143
-				throw $e;
1144
-			}
1145
-
1146
-			$arguments[0] = array_pad([], count($arguments[0]), $cr);
1147
-			$ret = $doMethod();
1148
-		}
1149
-		return $ret;
1150
-	}
1151
-
1152
-	/**
1153
-	 * retrieved. Results will according to the order in the array.
1154
-	 *
1155
-	 * @param $filter
1156
-	 * @param $base
1157
-	 * @param string[]|string|null $attr
1158
-	 * @param int $limit optional, maximum results to be counted
1159
-	 * @param int $offset optional, a starting point
1160
-	 * @return array|false array with the search result as first value and pagedSearchOK as
1161
-	 * second | false if not successful
1162
-	 * @throws ServerNotAvailableException
1163
-	 */
1164
-	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1165
-		if(!is_null($attr) && !is_array($attr)) {
1166
-			$attr = [mb_strtolower($attr, 'UTF-8')];
1167
-		}
1168
-
1169
-		// See if we have a resource, in case not cancel with message
1170
-		$cr = $this->connection->getConnectionResource();
1171
-		if(!$this->ldap->isResource($cr)) {
1172
-			// Seems like we didn't find any resource.
1173
-			// Return an empty array just like before.
1174
-			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', ILogger::DEBUG);
1175
-			return false;
1176
-		}
1177
-
1178
-		//check whether paged search should be attempted
1179
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, $offset);
1180
-
1181
-		$linkResources = array_pad([], count($base), $cr);
1182
-		$sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1183
-		// cannot use $cr anymore, might have changed in the previous call!
1184
-		$error = $this->ldap->errno($this->connection->getConnectionResource());
1185
-		if(!is_array($sr) || $error !== 0) {
1186
-			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), ILogger::ERROR);
1187
-			return false;
1188
-		}
1189
-
1190
-		return [$sr, $pagedSearchOK];
1191
-	}
1192
-
1193
-	/**
1194
-	 * processes an LDAP paged search operation
1195
-	 *
1196
-	 * @param array $sr the array containing the LDAP search resources
1197
-	 * @param string $filter the LDAP filter for the search
1198
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1199
-	 * @param int $iFoundItems number of results in the single search operation
1200
-	 * @param int $limit maximum results to be counted
1201
-	 * @param int $offset a starting point
1202
-	 * @param bool $pagedSearchOK whether a paged search has been executed
1203
-	 * @param bool $skipHandling required for paged search when cookies to
1204
-	 * prior results need to be gained
1205
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
1206
-	 * @throws ServerNotAvailableException
1207
-	 */
1208
-	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1209
-		$cookie = null;
1210
-		if($pagedSearchOK) {
1211
-			$cr = $this->connection->getConnectionResource();
1212
-			foreach($sr as $key => $res) {
1213
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1214
-					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1215
-				}
1216
-			}
1217
-
1218
-			//browsing through prior pages to get the cookie for the new one
1219
-			if($skipHandling) {
1220
-				return false;
1221
-			}
1222
-			// if count is bigger, then the server does not support
1223
-			// paged search. Instead, he did a normal search. We set a
1224
-			// flag here, so the callee knows how to deal with it.
1225
-			if($iFoundItems <= $limit) {
1226
-				$this->pagedSearchedSuccessful = true;
1227
-			}
1228
-		} else {
1229
-			if(!is_null($limit) && (int)$this->connection->ldapPagingSize !== 0) {
1230
-				\OC::$server->getLogger()->debug(
1231
-					'Paged search was not available',
1232
-					[ 'app' => 'user_ldap' ]
1233
-				);
1234
-			}
1235
-		}
1236
-		/* ++ Fixing RHDS searches with pages with zero results ++
1136
+            \OCP\Util::writeLog('user_ldap', "Connection lost on $command, attempting to reestablish.", ILogger::DEBUG);
1137
+            $this->connection->resetConnectionResource();
1138
+            $cr = $this->connection->getConnectionResource();
1139
+
1140
+            if(!$this->ldap->isResource($cr)) {
1141
+                // Seems like we didn't find any resource.
1142
+                \OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", ILogger::DEBUG);
1143
+                throw $e;
1144
+            }
1145
+
1146
+            $arguments[0] = array_pad([], count($arguments[0]), $cr);
1147
+            $ret = $doMethod();
1148
+        }
1149
+        return $ret;
1150
+    }
1151
+
1152
+    /**
1153
+     * retrieved. Results will according to the order in the array.
1154
+     *
1155
+     * @param $filter
1156
+     * @param $base
1157
+     * @param string[]|string|null $attr
1158
+     * @param int $limit optional, maximum results to be counted
1159
+     * @param int $offset optional, a starting point
1160
+     * @return array|false array with the search result as first value and pagedSearchOK as
1161
+     * second | false if not successful
1162
+     * @throws ServerNotAvailableException
1163
+     */
1164
+    private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1165
+        if(!is_null($attr) && !is_array($attr)) {
1166
+            $attr = [mb_strtolower($attr, 'UTF-8')];
1167
+        }
1168
+
1169
+        // See if we have a resource, in case not cancel with message
1170
+        $cr = $this->connection->getConnectionResource();
1171
+        if(!$this->ldap->isResource($cr)) {
1172
+            // Seems like we didn't find any resource.
1173
+            // Return an empty array just like before.
1174
+            \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', ILogger::DEBUG);
1175
+            return false;
1176
+        }
1177
+
1178
+        //check whether paged search should be attempted
1179
+        $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, $offset);
1180
+
1181
+        $linkResources = array_pad([], count($base), $cr);
1182
+        $sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1183
+        // cannot use $cr anymore, might have changed in the previous call!
1184
+        $error = $this->ldap->errno($this->connection->getConnectionResource());
1185
+        if(!is_array($sr) || $error !== 0) {
1186
+            \OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), ILogger::ERROR);
1187
+            return false;
1188
+        }
1189
+
1190
+        return [$sr, $pagedSearchOK];
1191
+    }
1192
+
1193
+    /**
1194
+     * processes an LDAP paged search operation
1195
+     *
1196
+     * @param array $sr the array containing the LDAP search resources
1197
+     * @param string $filter the LDAP filter for the search
1198
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1199
+     * @param int $iFoundItems number of results in the single search operation
1200
+     * @param int $limit maximum results to be counted
1201
+     * @param int $offset a starting point
1202
+     * @param bool $pagedSearchOK whether a paged search has been executed
1203
+     * @param bool $skipHandling required for paged search when cookies to
1204
+     * prior results need to be gained
1205
+     * @return bool cookie validity, true if we have more pages, false otherwise.
1206
+     * @throws ServerNotAvailableException
1207
+     */
1208
+    private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1209
+        $cookie = null;
1210
+        if($pagedSearchOK) {
1211
+            $cr = $this->connection->getConnectionResource();
1212
+            foreach($sr as $key => $res) {
1213
+                if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1214
+                    $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1215
+                }
1216
+            }
1217
+
1218
+            //browsing through prior pages to get the cookie for the new one
1219
+            if($skipHandling) {
1220
+                return false;
1221
+            }
1222
+            // if count is bigger, then the server does not support
1223
+            // paged search. Instead, he did a normal search. We set a
1224
+            // flag here, so the callee knows how to deal with it.
1225
+            if($iFoundItems <= $limit) {
1226
+                $this->pagedSearchedSuccessful = true;
1227
+            }
1228
+        } else {
1229
+            if(!is_null($limit) && (int)$this->connection->ldapPagingSize !== 0) {
1230
+                \OC::$server->getLogger()->debug(
1231
+                    'Paged search was not available',
1232
+                    [ 'app' => 'user_ldap' ]
1233
+                );
1234
+            }
1235
+        }
1236
+        /* ++ Fixing RHDS searches with pages with zero results ++
1237 1237
 		 * Return cookie status. If we don't have more pages, with RHDS
1238 1238
 		 * cookie is null, with openldap cookie is an empty string and
1239 1239
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
1240 1240
 		 */
1241
-		return !empty($cookie) || $cookie === '0';
1242
-	}
1243
-
1244
-	/**
1245
-	 * executes an LDAP search, but counts the results only
1246
-	 *
1247
-	 * @param string $filter the LDAP filter for the search
1248
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1249
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1250
-	 * retrieved. Results will according to the order in the array.
1251
-	 * @param int $limit optional, maximum results to be counted
1252
-	 * @param int $offset optional, a starting point
1253
-	 * @param bool $skipHandling indicates whether the pages search operation is
1254
-	 * completed
1255
-	 * @return int|false Integer or false if the search could not be initialized
1256
-	 * @throws ServerNotAvailableException
1257
-	 */
1258
-	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1259
-		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), ILogger::DEBUG);
1260
-
1261
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1262
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1263
-			$limitPerPage = $limit;
1264
-		}
1265
-
1266
-		$counter = 0;
1267
-		$count = null;
1268
-		$this->connection->getConnectionResource();
1269
-
1270
-		do {
1271
-			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1272
-			if($search === false) {
1273
-				return $counter > 0 ? $counter : false;
1274
-			}
1275
-			list($sr, $pagedSearchOK) = $search;
1276
-
1277
-			/* ++ Fixing RHDS searches with pages with zero results ++
1241
+        return !empty($cookie) || $cookie === '0';
1242
+    }
1243
+
1244
+    /**
1245
+     * executes an LDAP search, but counts the results only
1246
+     *
1247
+     * @param string $filter the LDAP filter for the search
1248
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1249
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1250
+     * retrieved. Results will according to the order in the array.
1251
+     * @param int $limit optional, maximum results to be counted
1252
+     * @param int $offset optional, a starting point
1253
+     * @param bool $skipHandling indicates whether the pages search operation is
1254
+     * completed
1255
+     * @return int|false Integer or false if the search could not be initialized
1256
+     * @throws ServerNotAvailableException
1257
+     */
1258
+    private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1259
+        \OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), ILogger::DEBUG);
1260
+
1261
+        $limitPerPage = (int)$this->connection->ldapPagingSize;
1262
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1263
+            $limitPerPage = $limit;
1264
+        }
1265
+
1266
+        $counter = 0;
1267
+        $count = null;
1268
+        $this->connection->getConnectionResource();
1269
+
1270
+        do {
1271
+            $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1272
+            if($search === false) {
1273
+                return $counter > 0 ? $counter : false;
1274
+            }
1275
+            list($sr, $pagedSearchOK) = $search;
1276
+
1277
+            /* ++ Fixing RHDS searches with pages with zero results ++
1278 1278
 			 * countEntriesInSearchResults() method signature changed
1279 1279
 			 * by removing $limit and &$hasHitLimit parameters
1280 1280
 			 */
1281
-			$count = $this->countEntriesInSearchResults($sr);
1282
-			$counter += $count;
1281
+            $count = $this->countEntriesInSearchResults($sr);
1282
+            $counter += $count;
1283 1283
 
1284
-			$hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1285
-										$offset, $pagedSearchOK, $skipHandling);
1286
-			$offset += $limitPerPage;
1287
-			/* ++ Fixing RHDS searches with pages with zero results ++
1284
+            $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1285
+                                        $offset, $pagedSearchOK, $skipHandling);
1286
+            $offset += $limitPerPage;
1287
+            /* ++ Fixing RHDS searches with pages with zero results ++
1288 1288
 			 * Continue now depends on $hasMorePages value
1289 1289
 			 */
1290
-			$continue = $pagedSearchOK && $hasMorePages;
1291
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1292
-
1293
-		return $counter;
1294
-	}
1295
-
1296
-	/**
1297
-	 * @param array $searchResults
1298
-	 * @return int
1299
-	 * @throws ServerNotAvailableException
1300
-	 */
1301
-	private function countEntriesInSearchResults($searchResults) {
1302
-		$counter = 0;
1303
-
1304
-		foreach($searchResults as $res) {
1305
-			$count = (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res);
1306
-			$counter += $count;
1307
-		}
1308
-
1309
-		return $counter;
1310
-	}
1311
-
1312
-	/**
1313
-	 * Executes an LDAP search
1314
-	 *
1315
-	 * @param string $filter the LDAP filter for the search
1316
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1317
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1318
-	 * @param int $limit
1319
-	 * @param int $offset
1320
-	 * @param bool $skipHandling
1321
-	 * @return array with the search result
1322
-	 * @throws ServerNotAvailableException
1323
-	 */
1324
-	public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1325
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1326
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1327
-			$limitPerPage = $limit;
1328
-		}
1329
-
1330
-		/* ++ Fixing RHDS searches with pages with zero results ++
1290
+            $continue = $pagedSearchOK && $hasMorePages;
1291
+        } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1292
+
1293
+        return $counter;
1294
+    }
1295
+
1296
+    /**
1297
+     * @param array $searchResults
1298
+     * @return int
1299
+     * @throws ServerNotAvailableException
1300
+     */
1301
+    private function countEntriesInSearchResults($searchResults) {
1302
+        $counter = 0;
1303
+
1304
+        foreach($searchResults as $res) {
1305
+            $count = (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res);
1306
+            $counter += $count;
1307
+        }
1308
+
1309
+        return $counter;
1310
+    }
1311
+
1312
+    /**
1313
+     * Executes an LDAP search
1314
+     *
1315
+     * @param string $filter the LDAP filter for the search
1316
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1317
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1318
+     * @param int $limit
1319
+     * @param int $offset
1320
+     * @param bool $skipHandling
1321
+     * @return array with the search result
1322
+     * @throws ServerNotAvailableException
1323
+     */
1324
+    public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1325
+        $limitPerPage = (int)$this->connection->ldapPagingSize;
1326
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1327
+            $limitPerPage = $limit;
1328
+        }
1329
+
1330
+        /* ++ Fixing RHDS searches with pages with zero results ++
1331 1331
 		 * As we can have pages with zero results and/or pages with less
1332 1332
 		 * than $limit results but with a still valid server 'cookie',
1333 1333
 		 * loops through until we get $continue equals true and
1334 1334
 		 * $findings['count'] < $limit
1335 1335
 		 */
1336
-		$findings = [];
1337
-		$savedoffset = $offset;
1338
-		do {
1339
-			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1340
-			if($search === false) {
1341
-				return [];
1342
-			}
1343
-			list($sr, $pagedSearchOK) = $search;
1344
-			$cr = $this->connection->getConnectionResource();
1345
-
1346
-			if($skipHandling) {
1347
-				//i.e. result do not need to be fetched, we just need the cookie
1348
-				//thus pass 1 or any other value as $iFoundItems because it is not
1349
-				//used
1350
-				$this->processPagedSearchStatus($sr, $filter, $base, 1, $limitPerPage,
1351
-								$offset, $pagedSearchOK,
1352
-								$skipHandling);
1353
-				return [];
1354
-			}
1355
-
1356
-			$iFoundItems = 0;
1357
-			foreach($sr as $res) {
1358
-				$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1359
-				$iFoundItems = max($iFoundItems, $findings['count']);
1360
-				unset($findings['count']);
1361
-			}
1362
-
1363
-			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $iFoundItems,
1364
-				$limitPerPage, $offset, $pagedSearchOK,
1365
-										$skipHandling);
1366
-			$offset += $limitPerPage;
1367
-		} while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1368
-		// reseting offset
1369
-		$offset = $savedoffset;
1370
-
1371
-		// if we're here, probably no connection resource is returned.
1372
-		// to make Nextcloud behave nicely, we simply give back an empty array.
1373
-		if(is_null($findings)) {
1374
-			return [];
1375
-		}
1376
-
1377
-		if(!is_null($attr)) {
1378
-			$selection = [];
1379
-			$i = 0;
1380
-			foreach($findings as $item) {
1381
-				if(!is_array($item)) {
1382
-					continue;
1383
-				}
1384
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1385
-				foreach($attr as $key) {
1386
-					if(isset($item[$key])) {
1387
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1388
-							unset($item[$key]['count']);
1389
-						}
1390
-						if($key !== 'dn') {
1391
-							if($this->resemblesDN($key)) {
1392
-								$selection[$i][$key] = $this->helper->sanitizeDN($item[$key]);
1393
-							} else if($key === 'objectguid' || $key === 'guid') {
1394
-								$selection[$i][$key] = [$this->convertObjectGUID2Str($item[$key][0])];
1395
-							} else {
1396
-								$selection[$i][$key] = $item[$key];
1397
-							}
1398
-						} else {
1399
-							$selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1400
-						}
1401
-					}
1402
-
1403
-				}
1404
-				$i++;
1405
-			}
1406
-			$findings = $selection;
1407
-		}
1408
-		//we slice the findings, when
1409
-		//a) paged search unsuccessful, though attempted
1410
-		//b) no paged search, but limit set
1411
-		if((!$this->getPagedSearchResultState()
1412
-			&& $pagedSearchOK)
1413
-			|| (
1414
-				!$pagedSearchOK
1415
-				&& !is_null($limit)
1416
-			)
1417
-		) {
1418
-			$findings = array_slice($findings, (int)$offset, $limit);
1419
-		}
1420
-		return $findings;
1421
-	}
1422
-
1423
-	/**
1424
-	 * @param string $name
1425
-	 * @return string
1426
-	 * @throws \InvalidArgumentException
1427
-	 */
1428
-	public function sanitizeUsername($name) {
1429
-		$name = trim($name);
1430
-
1431
-		if($this->connection->ldapIgnoreNamingRules) {
1432
-			return $name;
1433
-		}
1434
-
1435
-		// Transliteration to ASCII
1436
-		$transliterated = @iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1437
-		if($transliterated !== false) {
1438
-			// depending on system config iconv can work or not
1439
-			$name = $transliterated;
1440
-		}
1441
-
1442
-		// Replacements
1443
-		$name = str_replace(' ', '_', $name);
1444
-
1445
-		// Every remaining disallowed characters will be removed
1446
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1447
-
1448
-		if($name === '') {
1449
-			throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1450
-		}
1451
-
1452
-		return $name;
1453
-	}
1454
-
1455
-	/**
1456
-	 * escapes (user provided) parts for LDAP filter
1457
-	 * @param string $input, the provided value
1458
-	 * @param bool $allowAsterisk whether in * at the beginning should be preserved
1459
-	 * @return string the escaped string
1460
-	 */
1461
-	public function escapeFilterPart($input, $allowAsterisk = false) {
1462
-		$asterisk = '';
1463
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1464
-			$asterisk = '*';
1465
-			$input = mb_substr($input, 1, null, 'UTF-8');
1466
-		}
1467
-		$search  = ['*', '\\', '(', ')'];
1468
-		$replace = ['\\*', '\\\\', '\\(', '\\)'];
1469
-		return $asterisk . str_replace($search, $replace, $input);
1470
-	}
1471
-
1472
-	/**
1473
-	 * combines the input filters with AND
1474
-	 * @param string[] $filters the filters to connect
1475
-	 * @return string the combined filter
1476
-	 */
1477
-	public function combineFilterWithAnd($filters) {
1478
-		return $this->combineFilter($filters, '&');
1479
-	}
1480
-
1481
-	/**
1482
-	 * combines the input filters with OR
1483
-	 * @param string[] $filters the filters to connect
1484
-	 * @return string the combined filter
1485
-	 * Combines Filter arguments with OR
1486
-	 */
1487
-	public function combineFilterWithOr($filters) {
1488
-		return $this->combineFilter($filters, '|');
1489
-	}
1490
-
1491
-	/**
1492
-	 * combines the input filters with given operator
1493
-	 * @param string[] $filters the filters to connect
1494
-	 * @param string $operator either & or |
1495
-	 * @return string the combined filter
1496
-	 */
1497
-	private function combineFilter($filters, $operator) {
1498
-		$combinedFilter = '('.$operator;
1499
-		foreach($filters as $filter) {
1500
-			if ($filter !== '' && $filter[0] !== '(') {
1501
-				$filter = '('.$filter.')';
1502
-			}
1503
-			$combinedFilter.=$filter;
1504
-		}
1505
-		$combinedFilter.=')';
1506
-		return $combinedFilter;
1507
-	}
1508
-
1509
-	/**
1510
-	 * creates a filter part for to perform search for users
1511
-	 * @param string $search the search term
1512
-	 * @return string the final filter part to use in LDAP searches
1513
-	 */
1514
-	public function getFilterPartForUserSearch($search) {
1515
-		return $this->getFilterPartForSearch($search,
1516
-			$this->connection->ldapAttributesForUserSearch,
1517
-			$this->connection->ldapUserDisplayName);
1518
-	}
1519
-
1520
-	/**
1521
-	 * creates a filter part for to perform search for groups
1522
-	 * @param string $search the search term
1523
-	 * @return string the final filter part to use in LDAP searches
1524
-	 */
1525
-	public function getFilterPartForGroupSearch($search) {
1526
-		return $this->getFilterPartForSearch($search,
1527
-			$this->connection->ldapAttributesForGroupSearch,
1528
-			$this->connection->ldapGroupDisplayName);
1529
-	}
1530
-
1531
-	/**
1532
-	 * creates a filter part for searches by splitting up the given search
1533
-	 * string into single words
1534
-	 * @param string $search the search term
1535
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1536
-	 * otherwise it does not make sense :)
1537
-	 * @return string the final filter part to use in LDAP searches
1538
-	 * @throws \Exception
1539
-	 */
1540
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1541
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1542
-			throw new \Exception('searchAttributes must be an array with at least two string');
1543
-		}
1544
-		$searchWords = explode(' ', trim($search));
1545
-		$wordFilters = [];
1546
-		foreach($searchWords as $word) {
1547
-			$word = $this->prepareSearchTerm($word);
1548
-			//every word needs to appear at least once
1549
-			$wordMatchOneAttrFilters = [];
1550
-			foreach($searchAttributes as $attr) {
1551
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1552
-			}
1553
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1554
-		}
1555
-		return $this->combineFilterWithAnd($wordFilters);
1556
-	}
1557
-
1558
-	/**
1559
-	 * creates a filter part for searches
1560
-	 * @param string $search the search term
1561
-	 * @param string[]|null $searchAttributes
1562
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1563
-	 * did not define search attributes. Typically the display name attribute.
1564
-	 * @return string the final filter part to use in LDAP searches
1565
-	 */
1566
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1567
-		$filter = [];
1568
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1569
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1570
-			try {
1571
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1572
-			} catch(\Exception $e) {
1573
-				\OCP\Util::writeLog(
1574
-					'user_ldap',
1575
-					'Creating advanced filter for search failed, falling back to simple method.',
1576
-					ILogger::INFO
1577
-				);
1578
-			}
1579
-		}
1580
-
1581
-		$search = $this->prepareSearchTerm($search);
1582
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1583
-			if ($fallbackAttribute === '') {
1584
-				return '';
1585
-			}
1586
-			$filter[] = $fallbackAttribute . '=' . $search;
1587
-		} else {
1588
-			foreach($searchAttributes as $attribute) {
1589
-				$filter[] = $attribute . '=' . $search;
1590
-			}
1591
-		}
1592
-		if(count($filter) === 1) {
1593
-			return '('.$filter[0].')';
1594
-		}
1595
-		return $this->combineFilterWithOr($filter);
1596
-	}
1597
-
1598
-	/**
1599
-	 * returns the search term depending on whether we are allowed
1600
-	 * list users found by ldap with the current input appended by
1601
-	 * a *
1602
-	 * @return string
1603
-	 */
1604
-	private function prepareSearchTerm($term) {
1605
-		$config = \OC::$server->getConfig();
1606
-
1607
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1608
-
1609
-		$result = $term;
1610
-		if ($term === '') {
1611
-			$result = '*';
1612
-		} else if ($allowEnum !== 'no') {
1613
-			$result = $term . '*';
1614
-		}
1615
-		return $result;
1616
-	}
1617
-
1618
-	/**
1619
-	 * returns the filter used for counting users
1620
-	 * @return string
1621
-	 */
1622
-	public function getFilterForUserCount() {
1623
-		$filter = $this->combineFilterWithAnd([
1624
-			$this->connection->ldapUserFilter,
1625
-			$this->connection->ldapUserDisplayName . '=*'
1626
-		]);
1627
-
1628
-		return $filter;
1629
-	}
1630
-
1631
-	/**
1632
-	 * @param string $name
1633
-	 * @param string $password
1634
-	 * @return bool
1635
-	 */
1636
-	public function areCredentialsValid($name, $password) {
1637
-		$name = $this->helper->DNasBaseParameter($name);
1638
-		$testConnection = clone $this->connection;
1639
-		$credentials = [
1640
-			'ldapAgentName' => $name,
1641
-			'ldapAgentPassword' => $password
1642
-		];
1643
-		if(!$testConnection->setConfiguration($credentials)) {
1644
-			return false;
1645
-		}
1646
-		return $testConnection->bind();
1647
-	}
1648
-
1649
-	/**
1650
-	 * reverse lookup of a DN given a known UUID
1651
-	 *
1652
-	 * @param string $uuid
1653
-	 * @return string
1654
-	 * @throws \Exception
1655
-	 */
1656
-	public function getUserDnByUuid($uuid) {
1657
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1658
-		$filter       = $this->connection->ldapUserFilter;
1659
-		$base         = $this->connection->ldapBaseUsers;
1660
-
1661
-		if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1662
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1663
-			// existing DN to be able to reliably detect it.
1664
-			$result = $this->search($filter, $base, ['dn'], 1);
1665
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1666
-				throw new \Exception('Cannot determine UUID attribute');
1667
-			}
1668
-			$dn = $result[0]['dn'][0];
1669
-			if(!$this->detectUuidAttribute($dn, true)) {
1670
-				throw new \Exception('Cannot determine UUID attribute');
1671
-			}
1672
-		} else {
1673
-			// The UUID attribute is either known or an override is given.
1674
-			// By calling this method we ensure that $this->connection->$uuidAttr
1675
-			// is definitely set
1676
-			if(!$this->detectUuidAttribute('', true)) {
1677
-				throw new \Exception('Cannot determine UUID attribute');
1678
-			}
1679
-		}
1680
-
1681
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1682
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1683
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1684
-		}
1685
-
1686
-		$filter = $uuidAttr . '=' . $uuid;
1687
-		$result = $this->searchUsers($filter, ['dn'], 2);
1688
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1689
-			// we put the count into account to make sure that this is
1690
-			// really unique
1691
-			return $result[0]['dn'][0];
1692
-		}
1693
-
1694
-		throw new \Exception('Cannot determine UUID attribute');
1695
-	}
1696
-
1697
-	/**
1698
-	 * auto-detects the directory's UUID attribute
1699
-	 *
1700
-	 * @param string $dn a known DN used to check against
1701
-	 * @param bool $isUser
1702
-	 * @param bool $force the detection should be run, even if it is not set to auto
1703
-	 * @param array|null $ldapRecord
1704
-	 * @return bool true on success, false otherwise
1705
-	 * @throws ServerNotAvailableException
1706
-	 */
1707
-	private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1708
-		if($isUser) {
1709
-			$uuidAttr     = 'ldapUuidUserAttribute';
1710
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1711
-		} else {
1712
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1713
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1714
-		}
1715
-
1716
-		if(!$force) {
1717
-			if($this->connection->$uuidAttr !== 'auto') {
1718
-				return true;
1719
-			} else if (is_string($uuidOverride) && trim($uuidOverride) !== '') {
1720
-				$this->connection->$uuidAttr = $uuidOverride;
1721
-				return true;
1722
-			}
1723
-
1724
-			$attribute = $this->connection->getFromCache($uuidAttr);
1725
-			if(!$attribute === null) {
1726
-				$this->connection->$uuidAttr = $attribute;
1727
-				return true;
1728
-			}
1729
-		}
1730
-
1731
-		foreach(self::UUID_ATTRIBUTES as $attribute) {
1732
-			if($ldapRecord !== null) {
1733
-				// we have the info from LDAP already, we don't need to talk to the server again
1734
-				if(isset($ldapRecord[$attribute])) {
1735
-					$this->connection->$uuidAttr = $attribute;
1736
-					return true;
1737
-				}
1738
-			}
1739
-
1740
-			$value = $this->readAttribute($dn, $attribute);
1741
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1742
-				\OC::$server->getLogger()->debug(
1743
-					'Setting {attribute} as {subject}',
1744
-					[
1745
-						'app' => 'user_ldap',
1746
-						'attribute' => $attribute,
1747
-						'subject' => $uuidAttr
1748
-					]
1749
-				);
1750
-				$this->connection->$uuidAttr = $attribute;
1751
-				$this->connection->writeToCache($uuidAttr, $attribute);
1752
-				return true;
1753
-			}
1754
-		}
1755
-		\OC::$server->getLogger()->debug('Could not autodetect the UUID attribute', ['app' => 'user_ldap']);
1756
-
1757
-		return false;
1758
-	}
1759
-
1760
-	/**
1761
-	 * @param string $dn
1762
-	 * @param bool $isUser
1763
-	 * @param null $ldapRecord
1764
-	 * @return bool|string
1765
-	 * @throws ServerNotAvailableException
1766
-	 */
1767
-	public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1768
-		if($isUser) {
1769
-			$uuidAttr     = 'ldapUuidUserAttribute';
1770
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1771
-		} else {
1772
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1773
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1774
-		}
1775
-
1776
-		$uuid = false;
1777
-		if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1778
-			$attr = $this->connection->$uuidAttr;
1779
-			$uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1780
-			if( !is_array($uuid)
1781
-				&& $uuidOverride !== ''
1782
-				&& $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1783
-			{
1784
-				$uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1785
-					? $ldapRecord[$this->connection->$uuidAttr]
1786
-					: $this->readAttribute($dn, $this->connection->$uuidAttr);
1787
-			}
1788
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1789
-				$uuid = $uuid[0];
1790
-			}
1791
-		}
1792
-
1793
-		return $uuid;
1794
-	}
1795
-
1796
-	/**
1797
-	 * converts a binary ObjectGUID into a string representation
1798
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1799
-	 * @return string
1800
-	 * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1801
-	 */
1802
-	private function convertObjectGUID2Str($oguid) {
1803
-		$hex_guid = bin2hex($oguid);
1804
-		$hex_guid_to_guid_str = '';
1805
-		for($k = 1; $k <= 4; ++$k) {
1806
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1807
-		}
1808
-		$hex_guid_to_guid_str .= '-';
1809
-		for($k = 1; $k <= 2; ++$k) {
1810
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1811
-		}
1812
-		$hex_guid_to_guid_str .= '-';
1813
-		for($k = 1; $k <= 2; ++$k) {
1814
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1815
-		}
1816
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1817
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1818
-
1819
-		return strtoupper($hex_guid_to_guid_str);
1820
-	}
1821
-
1822
-	/**
1823
-	 * the first three blocks of the string-converted GUID happen to be in
1824
-	 * reverse order. In order to use it in a filter, this needs to be
1825
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1826
-	 * to every two hax figures.
1827
-	 *
1828
-	 * If an invalid string is passed, it will be returned without change.
1829
-	 *
1830
-	 * @param string $guid
1831
-	 * @return string
1832
-	 */
1833
-	public function formatGuid2ForFilterUser($guid) {
1834
-		if(!is_string($guid)) {
1835
-			throw new \InvalidArgumentException('String expected');
1836
-		}
1837
-		$blocks = explode('-', $guid);
1838
-		if(count($blocks) !== 5) {
1839
-			/*
1336
+        $findings = [];
1337
+        $savedoffset = $offset;
1338
+        do {
1339
+            $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1340
+            if($search === false) {
1341
+                return [];
1342
+            }
1343
+            list($sr, $pagedSearchOK) = $search;
1344
+            $cr = $this->connection->getConnectionResource();
1345
+
1346
+            if($skipHandling) {
1347
+                //i.e. result do not need to be fetched, we just need the cookie
1348
+                //thus pass 1 or any other value as $iFoundItems because it is not
1349
+                //used
1350
+                $this->processPagedSearchStatus($sr, $filter, $base, 1, $limitPerPage,
1351
+                                $offset, $pagedSearchOK,
1352
+                                $skipHandling);
1353
+                return [];
1354
+            }
1355
+
1356
+            $iFoundItems = 0;
1357
+            foreach($sr as $res) {
1358
+                $findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1359
+                $iFoundItems = max($iFoundItems, $findings['count']);
1360
+                unset($findings['count']);
1361
+            }
1362
+
1363
+            $continue = $this->processPagedSearchStatus($sr, $filter, $base, $iFoundItems,
1364
+                $limitPerPage, $offset, $pagedSearchOK,
1365
+                                        $skipHandling);
1366
+            $offset += $limitPerPage;
1367
+        } while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1368
+        // reseting offset
1369
+        $offset = $savedoffset;
1370
+
1371
+        // if we're here, probably no connection resource is returned.
1372
+        // to make Nextcloud behave nicely, we simply give back an empty array.
1373
+        if(is_null($findings)) {
1374
+            return [];
1375
+        }
1376
+
1377
+        if(!is_null($attr)) {
1378
+            $selection = [];
1379
+            $i = 0;
1380
+            foreach($findings as $item) {
1381
+                if(!is_array($item)) {
1382
+                    continue;
1383
+                }
1384
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1385
+                foreach($attr as $key) {
1386
+                    if(isset($item[$key])) {
1387
+                        if(is_array($item[$key]) && isset($item[$key]['count'])) {
1388
+                            unset($item[$key]['count']);
1389
+                        }
1390
+                        if($key !== 'dn') {
1391
+                            if($this->resemblesDN($key)) {
1392
+                                $selection[$i][$key] = $this->helper->sanitizeDN($item[$key]);
1393
+                            } else if($key === 'objectguid' || $key === 'guid') {
1394
+                                $selection[$i][$key] = [$this->convertObjectGUID2Str($item[$key][0])];
1395
+                            } else {
1396
+                                $selection[$i][$key] = $item[$key];
1397
+                            }
1398
+                        } else {
1399
+                            $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1400
+                        }
1401
+                    }
1402
+
1403
+                }
1404
+                $i++;
1405
+            }
1406
+            $findings = $selection;
1407
+        }
1408
+        //we slice the findings, when
1409
+        //a) paged search unsuccessful, though attempted
1410
+        //b) no paged search, but limit set
1411
+        if((!$this->getPagedSearchResultState()
1412
+            && $pagedSearchOK)
1413
+            || (
1414
+                !$pagedSearchOK
1415
+                && !is_null($limit)
1416
+            )
1417
+        ) {
1418
+            $findings = array_slice($findings, (int)$offset, $limit);
1419
+        }
1420
+        return $findings;
1421
+    }
1422
+
1423
+    /**
1424
+     * @param string $name
1425
+     * @return string
1426
+     * @throws \InvalidArgumentException
1427
+     */
1428
+    public function sanitizeUsername($name) {
1429
+        $name = trim($name);
1430
+
1431
+        if($this->connection->ldapIgnoreNamingRules) {
1432
+            return $name;
1433
+        }
1434
+
1435
+        // Transliteration to ASCII
1436
+        $transliterated = @iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1437
+        if($transliterated !== false) {
1438
+            // depending on system config iconv can work or not
1439
+            $name = $transliterated;
1440
+        }
1441
+
1442
+        // Replacements
1443
+        $name = str_replace(' ', '_', $name);
1444
+
1445
+        // Every remaining disallowed characters will be removed
1446
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1447
+
1448
+        if($name === '') {
1449
+            throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1450
+        }
1451
+
1452
+        return $name;
1453
+    }
1454
+
1455
+    /**
1456
+     * escapes (user provided) parts for LDAP filter
1457
+     * @param string $input, the provided value
1458
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1459
+     * @return string the escaped string
1460
+     */
1461
+    public function escapeFilterPart($input, $allowAsterisk = false) {
1462
+        $asterisk = '';
1463
+        if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1464
+            $asterisk = '*';
1465
+            $input = mb_substr($input, 1, null, 'UTF-8');
1466
+        }
1467
+        $search  = ['*', '\\', '(', ')'];
1468
+        $replace = ['\\*', '\\\\', '\\(', '\\)'];
1469
+        return $asterisk . str_replace($search, $replace, $input);
1470
+    }
1471
+
1472
+    /**
1473
+     * combines the input filters with AND
1474
+     * @param string[] $filters the filters to connect
1475
+     * @return string the combined filter
1476
+     */
1477
+    public function combineFilterWithAnd($filters) {
1478
+        return $this->combineFilter($filters, '&');
1479
+    }
1480
+
1481
+    /**
1482
+     * combines the input filters with OR
1483
+     * @param string[] $filters the filters to connect
1484
+     * @return string the combined filter
1485
+     * Combines Filter arguments with OR
1486
+     */
1487
+    public function combineFilterWithOr($filters) {
1488
+        return $this->combineFilter($filters, '|');
1489
+    }
1490
+
1491
+    /**
1492
+     * combines the input filters with given operator
1493
+     * @param string[] $filters the filters to connect
1494
+     * @param string $operator either & or |
1495
+     * @return string the combined filter
1496
+     */
1497
+    private function combineFilter($filters, $operator) {
1498
+        $combinedFilter = '('.$operator;
1499
+        foreach($filters as $filter) {
1500
+            if ($filter !== '' && $filter[0] !== '(') {
1501
+                $filter = '('.$filter.')';
1502
+            }
1503
+            $combinedFilter.=$filter;
1504
+        }
1505
+        $combinedFilter.=')';
1506
+        return $combinedFilter;
1507
+    }
1508
+
1509
+    /**
1510
+     * creates a filter part for to perform search for users
1511
+     * @param string $search the search term
1512
+     * @return string the final filter part to use in LDAP searches
1513
+     */
1514
+    public function getFilterPartForUserSearch($search) {
1515
+        return $this->getFilterPartForSearch($search,
1516
+            $this->connection->ldapAttributesForUserSearch,
1517
+            $this->connection->ldapUserDisplayName);
1518
+    }
1519
+
1520
+    /**
1521
+     * creates a filter part for to perform search for groups
1522
+     * @param string $search the search term
1523
+     * @return string the final filter part to use in LDAP searches
1524
+     */
1525
+    public function getFilterPartForGroupSearch($search) {
1526
+        return $this->getFilterPartForSearch($search,
1527
+            $this->connection->ldapAttributesForGroupSearch,
1528
+            $this->connection->ldapGroupDisplayName);
1529
+    }
1530
+
1531
+    /**
1532
+     * creates a filter part for searches by splitting up the given search
1533
+     * string into single words
1534
+     * @param string $search the search term
1535
+     * @param string[] $searchAttributes needs to have at least two attributes,
1536
+     * otherwise it does not make sense :)
1537
+     * @return string the final filter part to use in LDAP searches
1538
+     * @throws \Exception
1539
+     */
1540
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1541
+        if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1542
+            throw new \Exception('searchAttributes must be an array with at least two string');
1543
+        }
1544
+        $searchWords = explode(' ', trim($search));
1545
+        $wordFilters = [];
1546
+        foreach($searchWords as $word) {
1547
+            $word = $this->prepareSearchTerm($word);
1548
+            //every word needs to appear at least once
1549
+            $wordMatchOneAttrFilters = [];
1550
+            foreach($searchAttributes as $attr) {
1551
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1552
+            }
1553
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1554
+        }
1555
+        return $this->combineFilterWithAnd($wordFilters);
1556
+    }
1557
+
1558
+    /**
1559
+     * creates a filter part for searches
1560
+     * @param string $search the search term
1561
+     * @param string[]|null $searchAttributes
1562
+     * @param string $fallbackAttribute a fallback attribute in case the user
1563
+     * did not define search attributes. Typically the display name attribute.
1564
+     * @return string the final filter part to use in LDAP searches
1565
+     */
1566
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1567
+        $filter = [];
1568
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1569
+        if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1570
+            try {
1571
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1572
+            } catch(\Exception $e) {
1573
+                \OCP\Util::writeLog(
1574
+                    'user_ldap',
1575
+                    'Creating advanced filter for search failed, falling back to simple method.',
1576
+                    ILogger::INFO
1577
+                );
1578
+            }
1579
+        }
1580
+
1581
+        $search = $this->prepareSearchTerm($search);
1582
+        if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1583
+            if ($fallbackAttribute === '') {
1584
+                return '';
1585
+            }
1586
+            $filter[] = $fallbackAttribute . '=' . $search;
1587
+        } else {
1588
+            foreach($searchAttributes as $attribute) {
1589
+                $filter[] = $attribute . '=' . $search;
1590
+            }
1591
+        }
1592
+        if(count($filter) === 1) {
1593
+            return '('.$filter[0].')';
1594
+        }
1595
+        return $this->combineFilterWithOr($filter);
1596
+    }
1597
+
1598
+    /**
1599
+     * returns the search term depending on whether we are allowed
1600
+     * list users found by ldap with the current input appended by
1601
+     * a *
1602
+     * @return string
1603
+     */
1604
+    private function prepareSearchTerm($term) {
1605
+        $config = \OC::$server->getConfig();
1606
+
1607
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1608
+
1609
+        $result = $term;
1610
+        if ($term === '') {
1611
+            $result = '*';
1612
+        } else if ($allowEnum !== 'no') {
1613
+            $result = $term . '*';
1614
+        }
1615
+        return $result;
1616
+    }
1617
+
1618
+    /**
1619
+     * returns the filter used for counting users
1620
+     * @return string
1621
+     */
1622
+    public function getFilterForUserCount() {
1623
+        $filter = $this->combineFilterWithAnd([
1624
+            $this->connection->ldapUserFilter,
1625
+            $this->connection->ldapUserDisplayName . '=*'
1626
+        ]);
1627
+
1628
+        return $filter;
1629
+    }
1630
+
1631
+    /**
1632
+     * @param string $name
1633
+     * @param string $password
1634
+     * @return bool
1635
+     */
1636
+    public function areCredentialsValid($name, $password) {
1637
+        $name = $this->helper->DNasBaseParameter($name);
1638
+        $testConnection = clone $this->connection;
1639
+        $credentials = [
1640
+            'ldapAgentName' => $name,
1641
+            'ldapAgentPassword' => $password
1642
+        ];
1643
+        if(!$testConnection->setConfiguration($credentials)) {
1644
+            return false;
1645
+        }
1646
+        return $testConnection->bind();
1647
+    }
1648
+
1649
+    /**
1650
+     * reverse lookup of a DN given a known UUID
1651
+     *
1652
+     * @param string $uuid
1653
+     * @return string
1654
+     * @throws \Exception
1655
+     */
1656
+    public function getUserDnByUuid($uuid) {
1657
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1658
+        $filter       = $this->connection->ldapUserFilter;
1659
+        $base         = $this->connection->ldapBaseUsers;
1660
+
1661
+        if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1662
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1663
+            // existing DN to be able to reliably detect it.
1664
+            $result = $this->search($filter, $base, ['dn'], 1);
1665
+            if(!isset($result[0]) || !isset($result[0]['dn'])) {
1666
+                throw new \Exception('Cannot determine UUID attribute');
1667
+            }
1668
+            $dn = $result[0]['dn'][0];
1669
+            if(!$this->detectUuidAttribute($dn, true)) {
1670
+                throw new \Exception('Cannot determine UUID attribute');
1671
+            }
1672
+        } else {
1673
+            // The UUID attribute is either known or an override is given.
1674
+            // By calling this method we ensure that $this->connection->$uuidAttr
1675
+            // is definitely set
1676
+            if(!$this->detectUuidAttribute('', true)) {
1677
+                throw new \Exception('Cannot determine UUID attribute');
1678
+            }
1679
+        }
1680
+
1681
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1682
+        if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1683
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1684
+        }
1685
+
1686
+        $filter = $uuidAttr . '=' . $uuid;
1687
+        $result = $this->searchUsers($filter, ['dn'], 2);
1688
+        if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1689
+            // we put the count into account to make sure that this is
1690
+            // really unique
1691
+            return $result[0]['dn'][0];
1692
+        }
1693
+
1694
+        throw new \Exception('Cannot determine UUID attribute');
1695
+    }
1696
+
1697
+    /**
1698
+     * auto-detects the directory's UUID attribute
1699
+     *
1700
+     * @param string $dn a known DN used to check against
1701
+     * @param bool $isUser
1702
+     * @param bool $force the detection should be run, even if it is not set to auto
1703
+     * @param array|null $ldapRecord
1704
+     * @return bool true on success, false otherwise
1705
+     * @throws ServerNotAvailableException
1706
+     */
1707
+    private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1708
+        if($isUser) {
1709
+            $uuidAttr     = 'ldapUuidUserAttribute';
1710
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1711
+        } else {
1712
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1713
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1714
+        }
1715
+
1716
+        if(!$force) {
1717
+            if($this->connection->$uuidAttr !== 'auto') {
1718
+                return true;
1719
+            } else if (is_string($uuidOverride) && trim($uuidOverride) !== '') {
1720
+                $this->connection->$uuidAttr = $uuidOverride;
1721
+                return true;
1722
+            }
1723
+
1724
+            $attribute = $this->connection->getFromCache($uuidAttr);
1725
+            if(!$attribute === null) {
1726
+                $this->connection->$uuidAttr = $attribute;
1727
+                return true;
1728
+            }
1729
+        }
1730
+
1731
+        foreach(self::UUID_ATTRIBUTES as $attribute) {
1732
+            if($ldapRecord !== null) {
1733
+                // we have the info from LDAP already, we don't need to talk to the server again
1734
+                if(isset($ldapRecord[$attribute])) {
1735
+                    $this->connection->$uuidAttr = $attribute;
1736
+                    return true;
1737
+                }
1738
+            }
1739
+
1740
+            $value = $this->readAttribute($dn, $attribute);
1741
+            if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1742
+                \OC::$server->getLogger()->debug(
1743
+                    'Setting {attribute} as {subject}',
1744
+                    [
1745
+                        'app' => 'user_ldap',
1746
+                        'attribute' => $attribute,
1747
+                        'subject' => $uuidAttr
1748
+                    ]
1749
+                );
1750
+                $this->connection->$uuidAttr = $attribute;
1751
+                $this->connection->writeToCache($uuidAttr, $attribute);
1752
+                return true;
1753
+            }
1754
+        }
1755
+        \OC::$server->getLogger()->debug('Could not autodetect the UUID attribute', ['app' => 'user_ldap']);
1756
+
1757
+        return false;
1758
+    }
1759
+
1760
+    /**
1761
+     * @param string $dn
1762
+     * @param bool $isUser
1763
+     * @param null $ldapRecord
1764
+     * @return bool|string
1765
+     * @throws ServerNotAvailableException
1766
+     */
1767
+    public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1768
+        if($isUser) {
1769
+            $uuidAttr     = 'ldapUuidUserAttribute';
1770
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1771
+        } else {
1772
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1773
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1774
+        }
1775
+
1776
+        $uuid = false;
1777
+        if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1778
+            $attr = $this->connection->$uuidAttr;
1779
+            $uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1780
+            if( !is_array($uuid)
1781
+                && $uuidOverride !== ''
1782
+                && $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1783
+            {
1784
+                $uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1785
+                    ? $ldapRecord[$this->connection->$uuidAttr]
1786
+                    : $this->readAttribute($dn, $this->connection->$uuidAttr);
1787
+            }
1788
+            if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1789
+                $uuid = $uuid[0];
1790
+            }
1791
+        }
1792
+
1793
+        return $uuid;
1794
+    }
1795
+
1796
+    /**
1797
+     * converts a binary ObjectGUID into a string representation
1798
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1799
+     * @return string
1800
+     * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1801
+     */
1802
+    private function convertObjectGUID2Str($oguid) {
1803
+        $hex_guid = bin2hex($oguid);
1804
+        $hex_guid_to_guid_str = '';
1805
+        for($k = 1; $k <= 4; ++$k) {
1806
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1807
+        }
1808
+        $hex_guid_to_guid_str .= '-';
1809
+        for($k = 1; $k <= 2; ++$k) {
1810
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1811
+        }
1812
+        $hex_guid_to_guid_str .= '-';
1813
+        for($k = 1; $k <= 2; ++$k) {
1814
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1815
+        }
1816
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1817
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1818
+
1819
+        return strtoupper($hex_guid_to_guid_str);
1820
+    }
1821
+
1822
+    /**
1823
+     * the first three blocks of the string-converted GUID happen to be in
1824
+     * reverse order. In order to use it in a filter, this needs to be
1825
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1826
+     * to every two hax figures.
1827
+     *
1828
+     * If an invalid string is passed, it will be returned without change.
1829
+     *
1830
+     * @param string $guid
1831
+     * @return string
1832
+     */
1833
+    public function formatGuid2ForFilterUser($guid) {
1834
+        if(!is_string($guid)) {
1835
+            throw new \InvalidArgumentException('String expected');
1836
+        }
1837
+        $blocks = explode('-', $guid);
1838
+        if(count($blocks) !== 5) {
1839
+            /*
1840 1840
 			 * Why not throw an Exception instead? This method is a utility
1841 1841
 			 * called only when trying to figure out whether a "missing" known
1842 1842
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1847,283 +1847,283 @@  discard block
 block discarded – undo
1847 1847
 			 * an exception here would kill the experience for a valid, acting
1848 1848
 			 * user. Instead we write a log message.
1849 1849
 			 */
1850
-			\OC::$server->getLogger()->info(
1851
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1852
-				'({uuid}) probably does not match UUID configuration.',
1853
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1854
-			);
1855
-			return $guid;
1856
-		}
1857
-		for($i=0; $i < 3; $i++) {
1858
-			$pairs = str_split($blocks[$i], 2);
1859
-			$pairs = array_reverse($pairs);
1860
-			$blocks[$i] = implode('', $pairs);
1861
-		}
1862
-		for($i=0; $i < 5; $i++) {
1863
-			$pairs = str_split($blocks[$i], 2);
1864
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1865
-		}
1866
-		return implode('', $blocks);
1867
-	}
1868
-
1869
-	/**
1870
-	 * gets a SID of the domain of the given dn
1871
-	 *
1872
-	 * @param string $dn
1873
-	 * @return string|bool
1874
-	 * @throws ServerNotAvailableException
1875
-	 */
1876
-	public function getSID($dn) {
1877
-		$domainDN = $this->getDomainDNFromDN($dn);
1878
-		$cacheKey = 'getSID-'.$domainDN;
1879
-		$sid = $this->connection->getFromCache($cacheKey);
1880
-		if(!is_null($sid)) {
1881
-			return $sid;
1882
-		}
1883
-
1884
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1885
-		if(!is_array($objectSid) || empty($objectSid)) {
1886
-			$this->connection->writeToCache($cacheKey, false);
1887
-			return false;
1888
-		}
1889
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1890
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1891
-
1892
-		return $domainObjectSid;
1893
-	}
1894
-
1895
-	/**
1896
-	 * converts a binary SID into a string representation
1897
-	 * @param string $sid
1898
-	 * @return string
1899
-	 */
1900
-	public function convertSID2Str($sid) {
1901
-		// The format of a SID binary string is as follows:
1902
-		// 1 byte for the revision level
1903
-		// 1 byte for the number n of variable sub-ids
1904
-		// 6 bytes for identifier authority value
1905
-		// n*4 bytes for n sub-ids
1906
-		//
1907
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1908
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1909
-		$revision = ord($sid[0]);
1910
-		$numberSubID = ord($sid[1]);
1911
-
1912
-		$subIdStart = 8; // 1 + 1 + 6
1913
-		$subIdLength = 4;
1914
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1915
-			// Incorrect number of bytes present.
1916
-			return '';
1917
-		}
1918
-
1919
-		// 6 bytes = 48 bits can be represented using floats without loss of
1920
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1921
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1922
-
1923
-		$subIDs = [];
1924
-		for ($i = 0; $i < $numberSubID; $i++) {
1925
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1926
-			$subIDs[] = sprintf('%u', $subID[1]);
1927
-		}
1928
-
1929
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1930
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1931
-	}
1932
-
1933
-	/**
1934
-	 * checks if the given DN is part of the given base DN(s)
1935
-	 * @param string $dn the DN
1936
-	 * @param string[] $bases array containing the allowed base DN or DNs
1937
-	 * @return bool
1938
-	 */
1939
-	public function isDNPartOfBase($dn, $bases) {
1940
-		$belongsToBase = false;
1941
-		$bases = $this->helper->sanitizeDN($bases);
1942
-
1943
-		foreach($bases as $base) {
1944
-			$belongsToBase = true;
1945
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1946
-				$belongsToBase = false;
1947
-			}
1948
-			if($belongsToBase) {
1949
-				break;
1950
-			}
1951
-		}
1952
-		return $belongsToBase;
1953
-	}
1954
-
1955
-	/**
1956
-	 * resets a running Paged Search operation
1957
-	 *
1958
-	 * @throws ServerNotAvailableException
1959
-	 */
1960
-	private function abandonPagedSearch() {
1961
-		$cr = $this->connection->getConnectionResource();
1962
-		$this->invokeLDAPMethod('controlPagedResult', $cr, 0, false, $this->lastCookie);
1963
-		$this->getPagedSearchResultState();
1964
-		$this->lastCookie = '';
1965
-		$this->cookies = [];
1966
-	}
1967
-
1968
-	/**
1969
-	 * get a cookie for the next LDAP paged search
1970
-	 * @param string $base a string with the base DN for the search
1971
-	 * @param string $filter the search filter to identify the correct search
1972
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1973
-	 * @param int $offset the offset for the new search to identify the correct search really good
1974
-	 * @return string containing the key or empty if none is cached
1975
-	 */
1976
-	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1977
-		if($offset === 0) {
1978
-			return '';
1979
-		}
1980
-		$offset -= $limit;
1981
-		//we work with cache here
1982
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
1983
-		$cookie = '';
1984
-		if(isset($this->cookies[$cacheKey])) {
1985
-			$cookie = $this->cookies[$cacheKey];
1986
-			if(is_null($cookie)) {
1987
-				$cookie = '';
1988
-			}
1989
-		}
1990
-		return $cookie;
1991
-	}
1992
-
1993
-	/**
1994
-	 * checks whether an LDAP paged search operation has more pages that can be
1995
-	 * retrieved, typically when offset and limit are provided.
1996
-	 *
1997
-	 * Be very careful to use it: the last cookie value, which is inspected, can
1998
-	 * be reset by other operations. Best, call it immediately after a search(),
1999
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
2000
-	 * well. Don't rely on it with any fetchList-method.
2001
-	 * @return bool
2002
-	 */
2003
-	public function hasMoreResults() {
2004
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
2005
-			// as in RFC 2696, when all results are returned, the cookie will
2006
-			// be empty.
2007
-			return false;
2008
-		}
2009
-
2010
-		return true;
2011
-	}
2012
-
2013
-	/**
2014
-	 * set a cookie for LDAP paged search run
2015
-	 * @param string $base a string with the base DN for the search
2016
-	 * @param string $filter the search filter to identify the correct search
2017
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
2018
-	 * @param int $offset the offset for the run search to identify the correct search really good
2019
-	 * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
2020
-	 * @return void
2021
-	 */
2022
-	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
2023
-		// allow '0' for 389ds
2024
-		if(!empty($cookie) || $cookie === '0') {
2025
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
2026
-			$this->cookies[$cacheKey] = $cookie;
2027
-			$this->lastCookie = $cookie;
2028
-		}
2029
-	}
2030
-
2031
-	/**
2032
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
2033
-	 * @return boolean|null true on success, null or false otherwise
2034
-	 */
2035
-	public function getPagedSearchResultState() {
2036
-		$result = $this->pagedSearchedSuccessful;
2037
-		$this->pagedSearchedSuccessful = null;
2038
-		return $result;
2039
-	}
2040
-
2041
-	/**
2042
-	 * Prepares a paged search, if possible
2043
-	 *
2044
-	 * @param string $filter the LDAP filter for the search
2045
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
2046
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
2047
-	 * @param int $limit
2048
-	 * @param int $offset
2049
-	 * @return bool|true
2050
-	 * @throws ServerNotAvailableException
2051
-	 */
2052
-	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
2053
-		$pagedSearchOK = false;
2054
-		if ($limit !== 0) {
2055
-			$offset = (int)$offset; //can be null
2056
-			\OCP\Util::writeLog('user_ldap',
2057
-				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
2058
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
2059
-				ILogger::DEBUG);
2060
-			//get the cookie from the search for the previous search, required by LDAP
2061
-			foreach($bases as $base) {
2062
-
2063
-				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
2064
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
2065
-					// no cookie known from a potential previous search. We need
2066
-					// to start from 0 to come to the desired page. cookie value
2067
-					// of '0' is valid, because 389ds
2068
-					$reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
2069
-					$this->search($filter, [$base], $attr, $limit, $reOffset, true);
2070
-					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
2071
-					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
2072
-					// '0' is valid, because 389ds
2073
-					//TODO: remember this, probably does not change in the next request...
2074
-					if(empty($cookie) && $cookie !== '0') {
2075
-						$cookie = null;
2076
-					}
2077
-				}
2078
-				if(!is_null($cookie)) {
2079
-					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
2080
-					$this->abandonPagedSearch();
2081
-					$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2082
-						$this->connection->getConnectionResource(), $limit,
2083
-						false, $cookie);
2084
-					if(!$pagedSearchOK) {
2085
-						return false;
2086
-					}
2087
-					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', ILogger::DEBUG);
2088
-				} else {
2089
-					$e = new \Exception('No paged search possible, Limit '.$limit.' Offset '.$offset);
2090
-					\OC::$server->getLogger()->logException($e, ['level' => ILogger::DEBUG]);
2091
-				}
2092
-
2093
-			}
2094
-		/* ++ Fixing RHDS searches with pages with zero results ++
1850
+            \OC::$server->getLogger()->info(
1851
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1852
+                '({uuid}) probably does not match UUID configuration.',
1853
+                [ 'app' => 'user_ldap', 'uuid' => $guid ]
1854
+            );
1855
+            return $guid;
1856
+        }
1857
+        for($i=0; $i < 3; $i++) {
1858
+            $pairs = str_split($blocks[$i], 2);
1859
+            $pairs = array_reverse($pairs);
1860
+            $blocks[$i] = implode('', $pairs);
1861
+        }
1862
+        for($i=0; $i < 5; $i++) {
1863
+            $pairs = str_split($blocks[$i], 2);
1864
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1865
+        }
1866
+        return implode('', $blocks);
1867
+    }
1868
+
1869
+    /**
1870
+     * gets a SID of the domain of the given dn
1871
+     *
1872
+     * @param string $dn
1873
+     * @return string|bool
1874
+     * @throws ServerNotAvailableException
1875
+     */
1876
+    public function getSID($dn) {
1877
+        $domainDN = $this->getDomainDNFromDN($dn);
1878
+        $cacheKey = 'getSID-'.$domainDN;
1879
+        $sid = $this->connection->getFromCache($cacheKey);
1880
+        if(!is_null($sid)) {
1881
+            return $sid;
1882
+        }
1883
+
1884
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1885
+        if(!is_array($objectSid) || empty($objectSid)) {
1886
+            $this->connection->writeToCache($cacheKey, false);
1887
+            return false;
1888
+        }
1889
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1890
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1891
+
1892
+        return $domainObjectSid;
1893
+    }
1894
+
1895
+    /**
1896
+     * converts a binary SID into a string representation
1897
+     * @param string $sid
1898
+     * @return string
1899
+     */
1900
+    public function convertSID2Str($sid) {
1901
+        // The format of a SID binary string is as follows:
1902
+        // 1 byte for the revision level
1903
+        // 1 byte for the number n of variable sub-ids
1904
+        // 6 bytes for identifier authority value
1905
+        // n*4 bytes for n sub-ids
1906
+        //
1907
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1908
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1909
+        $revision = ord($sid[0]);
1910
+        $numberSubID = ord($sid[1]);
1911
+
1912
+        $subIdStart = 8; // 1 + 1 + 6
1913
+        $subIdLength = 4;
1914
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1915
+            // Incorrect number of bytes present.
1916
+            return '';
1917
+        }
1918
+
1919
+        // 6 bytes = 48 bits can be represented using floats without loss of
1920
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1921
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1922
+
1923
+        $subIDs = [];
1924
+        for ($i = 0; $i < $numberSubID; $i++) {
1925
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1926
+            $subIDs[] = sprintf('%u', $subID[1]);
1927
+        }
1928
+
1929
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1930
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1931
+    }
1932
+
1933
+    /**
1934
+     * checks if the given DN is part of the given base DN(s)
1935
+     * @param string $dn the DN
1936
+     * @param string[] $bases array containing the allowed base DN or DNs
1937
+     * @return bool
1938
+     */
1939
+    public function isDNPartOfBase($dn, $bases) {
1940
+        $belongsToBase = false;
1941
+        $bases = $this->helper->sanitizeDN($bases);
1942
+
1943
+        foreach($bases as $base) {
1944
+            $belongsToBase = true;
1945
+            if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1946
+                $belongsToBase = false;
1947
+            }
1948
+            if($belongsToBase) {
1949
+                break;
1950
+            }
1951
+        }
1952
+        return $belongsToBase;
1953
+    }
1954
+
1955
+    /**
1956
+     * resets a running Paged Search operation
1957
+     *
1958
+     * @throws ServerNotAvailableException
1959
+     */
1960
+    private function abandonPagedSearch() {
1961
+        $cr = $this->connection->getConnectionResource();
1962
+        $this->invokeLDAPMethod('controlPagedResult', $cr, 0, false, $this->lastCookie);
1963
+        $this->getPagedSearchResultState();
1964
+        $this->lastCookie = '';
1965
+        $this->cookies = [];
1966
+    }
1967
+
1968
+    /**
1969
+     * get a cookie for the next LDAP paged search
1970
+     * @param string $base a string with the base DN for the search
1971
+     * @param string $filter the search filter to identify the correct search
1972
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1973
+     * @param int $offset the offset for the new search to identify the correct search really good
1974
+     * @return string containing the key or empty if none is cached
1975
+     */
1976
+    private function getPagedResultCookie($base, $filter, $limit, $offset) {
1977
+        if($offset === 0) {
1978
+            return '';
1979
+        }
1980
+        $offset -= $limit;
1981
+        //we work with cache here
1982
+        $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
1983
+        $cookie = '';
1984
+        if(isset($this->cookies[$cacheKey])) {
1985
+            $cookie = $this->cookies[$cacheKey];
1986
+            if(is_null($cookie)) {
1987
+                $cookie = '';
1988
+            }
1989
+        }
1990
+        return $cookie;
1991
+    }
1992
+
1993
+    /**
1994
+     * checks whether an LDAP paged search operation has more pages that can be
1995
+     * retrieved, typically when offset and limit are provided.
1996
+     *
1997
+     * Be very careful to use it: the last cookie value, which is inspected, can
1998
+     * be reset by other operations. Best, call it immediately after a search(),
1999
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
2000
+     * well. Don't rely on it with any fetchList-method.
2001
+     * @return bool
2002
+     */
2003
+    public function hasMoreResults() {
2004
+        if(empty($this->lastCookie) && $this->lastCookie !== '0') {
2005
+            // as in RFC 2696, when all results are returned, the cookie will
2006
+            // be empty.
2007
+            return false;
2008
+        }
2009
+
2010
+        return true;
2011
+    }
2012
+
2013
+    /**
2014
+     * set a cookie for LDAP paged search run
2015
+     * @param string $base a string with the base DN for the search
2016
+     * @param string $filter the search filter to identify the correct search
2017
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
2018
+     * @param int $offset the offset for the run search to identify the correct search really good
2019
+     * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
2020
+     * @return void
2021
+     */
2022
+    private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
2023
+        // allow '0' for 389ds
2024
+        if(!empty($cookie) || $cookie === '0') {
2025
+            $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
2026
+            $this->cookies[$cacheKey] = $cookie;
2027
+            $this->lastCookie = $cookie;
2028
+        }
2029
+    }
2030
+
2031
+    /**
2032
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
2033
+     * @return boolean|null true on success, null or false otherwise
2034
+     */
2035
+    public function getPagedSearchResultState() {
2036
+        $result = $this->pagedSearchedSuccessful;
2037
+        $this->pagedSearchedSuccessful = null;
2038
+        return $result;
2039
+    }
2040
+
2041
+    /**
2042
+     * Prepares a paged search, if possible
2043
+     *
2044
+     * @param string $filter the LDAP filter for the search
2045
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
2046
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
2047
+     * @param int $limit
2048
+     * @param int $offset
2049
+     * @return bool|true
2050
+     * @throws ServerNotAvailableException
2051
+     */
2052
+    private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
2053
+        $pagedSearchOK = false;
2054
+        if ($limit !== 0) {
2055
+            $offset = (int)$offset; //can be null
2056
+            \OCP\Util::writeLog('user_ldap',
2057
+                'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
2058
+                .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
2059
+                ILogger::DEBUG);
2060
+            //get the cookie from the search for the previous search, required by LDAP
2061
+            foreach($bases as $base) {
2062
+
2063
+                $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
2064
+                if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
2065
+                    // no cookie known from a potential previous search. We need
2066
+                    // to start from 0 to come to the desired page. cookie value
2067
+                    // of '0' is valid, because 389ds
2068
+                    $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
2069
+                    $this->search($filter, [$base], $attr, $limit, $reOffset, true);
2070
+                    $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
2071
+                    //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
2072
+                    // '0' is valid, because 389ds
2073
+                    //TODO: remember this, probably does not change in the next request...
2074
+                    if(empty($cookie) && $cookie !== '0') {
2075
+                        $cookie = null;
2076
+                    }
2077
+                }
2078
+                if(!is_null($cookie)) {
2079
+                    //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
2080
+                    $this->abandonPagedSearch();
2081
+                    $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2082
+                        $this->connection->getConnectionResource(), $limit,
2083
+                        false, $cookie);
2084
+                    if(!$pagedSearchOK) {
2085
+                        return false;
2086
+                    }
2087
+                    \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', ILogger::DEBUG);
2088
+                } else {
2089
+                    $e = new \Exception('No paged search possible, Limit '.$limit.' Offset '.$offset);
2090
+                    \OC::$server->getLogger()->logException($e, ['level' => ILogger::DEBUG]);
2091
+                }
2092
+
2093
+            }
2094
+        /* ++ Fixing RHDS searches with pages with zero results ++
2095 2095
 		 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
2096 2096
 		 * due to pages with zero results.
2097 2097
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
2098 2098
 		 * if we don't have a previous paged search.
2099 2099
 		 */
2100
-		} else if ($limit === 0 && !empty($this->lastCookie)) {
2101
-			// a search without limit was requested. However, if we do use
2102
-			// Paged Search once, we always must do it. This requires us to
2103
-			// initialize it with the configured page size.
2104
-			$this->abandonPagedSearch();
2105
-			// in case someone set it to 0 … use 500, otherwise no results will
2106
-			// be returned.
2107
-			$pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
2108
-			$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2109
-				$this->connection->getConnectionResource(),
2110
-				$pageSize, false, '');
2111
-		}
2112
-
2113
-		return $pagedSearchOK;
2114
-	}
2115
-
2116
-	/**
2117
-	 * Is more than one $attr used for search?
2118
-	 *
2119
-	 * @param string|string[]|null $attr
2120
-	 * @return bool
2121
-	 */
2122
-	private function manyAttributes($attr): bool {
2123
-		if (\is_array($attr)) {
2124
-			return \count($attr) > 1;
2125
-		}
2126
-		return false;
2127
-	}
2100
+        } else if ($limit === 0 && !empty($this->lastCookie)) {
2101
+            // a search without limit was requested. However, if we do use
2102
+            // Paged Search once, we always must do it. This requires us to
2103
+            // initialize it with the configured page size.
2104
+            $this->abandonPagedSearch();
2105
+            // in case someone set it to 0 … use 500, otherwise no results will
2106
+            // be returned.
2107
+            $pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
2108
+            $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2109
+                $this->connection->getConnectionResource(),
2110
+                $pageSize, false, '');
2111
+        }
2112
+
2113
+        return $pagedSearchOK;
2114
+    }
2115
+
2116
+    /**
2117
+     * Is more than one $attr used for search?
2118
+     *
2119
+     * @param string|string[]|null $attr
2120
+     * @return bool
2121
+     */
2122
+    private function manyAttributes($attr): bool {
2123
+        if (\is_array($attr)) {
2124
+            return \count($attr) > 1;
2125
+        }
2126
+        return false;
2127
+    }
2128 2128
 
2129 2129
 }
Please login to merge, or discard this patch.
Spacing   +193 added lines, -193 removed lines patch added patch discarded remove patch
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
 	 * @return AbstractMapping
135 135
 	 */
136 136
 	public function getUserMapper() {
137
-		if(is_null($this->userMapper)) {
137
+		if (is_null($this->userMapper)) {
138 138
 			throw new \Exception('UserMapper was not assigned to this Access instance.');
139 139
 		}
140 140
 		return $this->userMapper;
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 	 * @return AbstractMapping
155 155
 	 */
156 156
 	public function getGroupMapper() {
157
-		if(is_null($this->groupMapper)) {
157
+		if (is_null($this->groupMapper)) {
158 158
 			throw new \Exception('GroupMapper was not assigned to this Access instance.');
159 159
 		}
160 160
 		return $this->groupMapper;
@@ -187,14 +187,14 @@  discard block
 block discarded – undo
187 187
 	 * @throws ServerNotAvailableException
188 188
 	 */
189 189
 	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
190
-		if(!$this->checkConnection()) {
190
+		if (!$this->checkConnection()) {
191 191
 			\OCP\Util::writeLog('user_ldap',
192 192
 				'No LDAP Connector assigned, access impossible for readAttribute.',
193 193
 				ILogger::WARN);
194 194
 			return false;
195 195
 		}
196 196
 		$cr = $this->connection->getConnectionResource();
197
-		if(!$this->ldap->isResource($cr)) {
197
+		if (!$this->ldap->isResource($cr)) {
198 198
 			//LDAP not available
199 199
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
200 200
 			return false;
@@ -204,7 +204,7 @@  discard block
 block discarded – undo
204 204
 		$this->abandonPagedSearch();
205 205
 		// openLDAP requires that we init a new Paged Search. Not needed by AD,
206 206
 		// but does not hurt either.
207
-		$pagingSize = (int)$this->connection->ldapPagingSize;
207
+		$pagingSize = (int) $this->connection->ldapPagingSize;
208 208
 		// 0 won't result in replies, small numbers may leave out groups
209 209
 		// (cf. #12306), 500 is default for paging and should work everywhere.
210 210
 		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
 		$isRangeRequest = false;
218 218
 		do {
219 219
 			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
220
-			if(is_bool($result)) {
220
+			if (is_bool($result)) {
221 221
 				// when an exists request was run and it was successful, an empty
222 222
 				// array must be returned
223 223
 				return $result ? [] : false;
@@ -234,22 +234,22 @@  discard block
 block discarded – undo
234 234
 			$result = $this->extractRangeData($result, $attr);
235 235
 			if (!empty($result)) {
236 236
 				$normalizedResult = $this->extractAttributeValuesFromResult(
237
-					[ $attr => $result['values'] ],
237
+					[$attr => $result['values']],
238 238
 					$attr
239 239
 				);
240 240
 				$values = array_merge($values, $normalizedResult);
241 241
 
242
-				if($result['rangeHigh'] === '*') {
242
+				if ($result['rangeHigh'] === '*') {
243 243
 					// when server replies with * as high range value, there are
244 244
 					// no more results left
245 245
 					return $values;
246 246
 				} else {
247
-					$low  = $result['rangeHigh'] + 1;
248
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
247
+					$low = $result['rangeHigh'] + 1;
248
+					$attrToRead = $result['attributeName'].';range='.$low.'-*';
249 249
 					$isRangeRequest = true;
250 250
 				}
251 251
 			}
252
-		} while($isRangeRequest);
252
+		} while ($isRangeRequest);
253 253
 
254 254
 		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, ILogger::DEBUG);
255 255
 		return false;
@@ -275,13 +275,13 @@  discard block
 block discarded – undo
275 275
 		if (!$this->ldap->isResource($rr)) {
276 276
 			if ($attribute !== '') {
277 277
 				//do not throw this message on userExists check, irritates
278
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, ILogger::DEBUG);
278
+				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, ILogger::DEBUG);
279 279
 			}
280 280
 			//in case an error occurs , e.g. object does not exist
281 281
 			return false;
282 282
 		}
283 283
 		if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
284
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', ILogger::DEBUG);
284
+			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', ILogger::DEBUG);
285 285
 			return true;
286 286
 		}
287 287
 		$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
@@ -306,12 +306,12 @@  discard block
 block discarded – undo
306 306
 	 */
307 307
 	public function extractAttributeValuesFromResult($result, $attribute) {
308 308
 		$values = [];
309
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
309
+		if (isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
310 310
 			$lowercaseAttribute = strtolower($attribute);
311
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
312
-				if($this->resemblesDN($attribute)) {
311
+			for ($i = 0; $i < $result[$attribute]['count']; $i++) {
312
+				if ($this->resemblesDN($attribute)) {
313 313
 					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
314
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
314
+				} elseif ($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
315 315
 					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
316 316
 				} else {
317 317
 					$values[] = $result[$attribute][$i];
@@ -333,10 +333,10 @@  discard block
 block discarded – undo
333 333
 	 */
334 334
 	public function extractRangeData($result, $attribute) {
335 335
 		$keys = array_keys($result);
336
-		foreach($keys as $key) {
337
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
336
+		foreach ($keys as $key) {
337
+			if ($key !== $attribute && strpos($key, $attribute) === 0) {
338 338
 				$queryData = explode(';', $key);
339
-				if(strpos($queryData[1], 'range=') === 0) {
339
+				if (strpos($queryData[1], 'range=') === 0) {
340 340
 					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
341 341
 					$data = [
342 342
 						'values' => $result[$key],
@@ -361,11 +361,11 @@  discard block
 block discarded – undo
361 361
 	 * @throws \Exception
362 362
 	 */
363 363
 	public function setPassword($userDN, $password) {
364
-		if((int)$this->connection->turnOnPasswordChange !== 1) {
364
+		if ((int) $this->connection->turnOnPasswordChange !== 1) {
365 365
 			throw new \Exception('LDAP password changes are disabled.');
366 366
 		}
367 367
 		$cr = $this->connection->getConnectionResource();
368
-		if(!$this->ldap->isResource($cr)) {
368
+		if (!$this->ldap->isResource($cr)) {
369 369
 			//LDAP not available
370 370
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', ILogger::DEBUG);
371 371
 			return false;
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
 			// try PASSWD extended operation first
375 375
 			return @$this->invokeLDAPMethod('exopPasswd', $cr, $userDN, '', $password) ||
376 376
 						@$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
377
-		} catch(ConstraintViolationException $e) {
377
+		} catch (ConstraintViolationException $e) {
378 378
 			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
379 379
 		}
380 380
 	}
@@ -416,17 +416,17 @@  discard block
 block discarded – undo
416 416
 	 */
417 417
 	public function getDomainDNFromDN($dn) {
418 418
 		$allParts = $this->ldap->explodeDN($dn, 0);
419
-		if($allParts === false) {
419
+		if ($allParts === false) {
420 420
 			//not a valid DN
421 421
 			return '';
422 422
 		}
423 423
 		$domainParts = [];
424 424
 		$dcFound = false;
425
-		foreach($allParts as $part) {
426
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
425
+		foreach ($allParts as $part) {
426
+			if (!$dcFound && strpos($part, 'dc=') === 0) {
427 427
 				$dcFound = true;
428 428
 			}
429
-			if($dcFound) {
429
+			if ($dcFound) {
430 430
 				$domainParts[] = $part;
431 431
 			}
432 432
 		}
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
 
453 453
 		//Check whether the DN belongs to the Base, to avoid issues on multi-
454 454
 		//server setups
455
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
455
+		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
456 456
 			return $fdn;
457 457
 		}
458 458
 
@@ -471,7 +471,7 @@  discard block
 block discarded – undo
471 471
 		//To avoid bypassing the base DN settings under certain circumstances
472 472
 		//with the group support, check whether the provided DN matches one of
473 473
 		//the given Bases
474
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
474
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
475 475
 			return false;
476 476
 		}
477 477
 
@@ -489,11 +489,11 @@  discard block
 block discarded – undo
489 489
 	 */
490 490
 	public function groupsMatchFilter($groupDNs) {
491 491
 		$validGroupDNs = [];
492
-		foreach($groupDNs as $dn) {
492
+		foreach ($groupDNs as $dn) {
493 493
 			$cacheKey = 'groupsMatchFilter-'.$dn;
494 494
 			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
495
-			if(!is_null($groupMatchFilter)) {
496
-				if($groupMatchFilter) {
495
+			if (!is_null($groupMatchFilter)) {
496
+				if ($groupMatchFilter) {
497 497
 					$validGroupDNs[] = $dn;
498 498
 				}
499 499
 				continue;
@@ -501,13 +501,13 @@  discard block
 block discarded – undo
501 501
 
502 502
 			// Check the base DN first. If this is not met already, we don't
503 503
 			// need to ask the server at all.
504
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
504
+			if (!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
505 505
 				$this->connection->writeToCache($cacheKey, false);
506 506
 				continue;
507 507
 			}
508 508
 
509 509
 			$result = $this->readAttribute($dn, '', $this->connection->ldapGroupFilter);
510
-			if(is_array($result)) {
510
+			if (is_array($result)) {
511 511
 				$this->connection->writeToCache($cacheKey, true);
512 512
 				$validGroupDNs[] = $dn;
513 513
 			} else {
@@ -530,7 +530,7 @@  discard block
 block discarded – undo
530 530
 		//To avoid bypassing the base DN settings under certain circumstances
531 531
 		//with the group support, check whether the provided DN matches one of
532 532
 		//the given Bases
533
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
533
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
534 534
 			return false;
535 535
 		}
536 536
 
@@ -550,7 +550,7 @@  discard block
 block discarded – undo
550 550
 	 */
551 551
 	public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
552 552
 		$newlyMapped = false;
553
-		if($isUser) {
553
+		if ($isUser) {
554 554
 			$mapper = $this->getUserMapper();
555 555
 			$nameAttribute = $this->connection->ldapUserDisplayName;
556 556
 			$filter = $this->connection->ldapUserFilter;
@@ -562,15 +562,15 @@  discard block
 block discarded – undo
562 562
 
563 563
 		//let's try to retrieve the Nextcloud name from the mappings table
564 564
 		$ncName = $mapper->getNameByDN($fdn);
565
-		if(is_string($ncName)) {
565
+		if (is_string($ncName)) {
566 566
 			return $ncName;
567 567
 		}
568 568
 
569 569
 		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
570 570
 		$uuid = $this->getUUID($fdn, $isUser, $record);
571
-		if(is_string($uuid)) {
571
+		if (is_string($uuid)) {
572 572
 			$ncName = $mapper->getNameByUUID($uuid);
573
-			if(is_string($ncName)) {
573
+			if (is_string($ncName)) {
574 574
 				$mapper->setDNbyUUID($fdn, $uuid);
575 575
 				return $ncName;
576 576
 			}
@@ -580,17 +580,17 @@  discard block
 block discarded – undo
580 580
 			return false;
581 581
 		}
582 582
 
583
-		if(is_null($ldapName)) {
583
+		if (is_null($ldapName)) {
584 584
 			$ldapName = $this->readAttribute($fdn, $nameAttribute, $filter);
585
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
585
+			if (!isset($ldapName[0]) && empty($ldapName[0])) {
586 586
 				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.' with filter '.$filter.'.', ILogger::INFO);
587 587
 				return false;
588 588
 			}
589 589
 			$ldapName = $ldapName[0];
590 590
 		}
591 591
 
592
-		if($isUser) {
593
-			$usernameAttribute = (string)$this->connection->ldapExpertUsernameAttr;
592
+		if ($isUser) {
593
+			$usernameAttribute = (string) $this->connection->ldapExpertUsernameAttr;
594 594
 			if ($usernameAttribute !== '') {
595 595
 				$username = $this->readAttribute($fdn, $usernameAttribute);
596 596
 				$username = $username[0];
@@ -620,14 +620,14 @@  discard block
 block discarded – undo
620 620
 		// outside of core user management will still cache the user as non-existing.
621 621
 		$originalTTL = $this->connection->ldapCacheTTL;
622 622
 		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
623
-		if( $intName !== ''
623
+		if ($intName !== ''
624 624
 			&& (($isUser && !$this->ncUserManager->userExists($intName))
625 625
 				|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))
626 626
 			)
627 627
 		) {
628 628
 			$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
629 629
 			$newlyMapped = $this->mapAndAnnounceIfApplicable($mapper, $fdn, $intName, $uuid, $isUser);
630
-			if($newlyMapped) {
630
+			if ($newlyMapped) {
631 631
 				return $intName;
632 632
 			}
633 633
 		}
@@ -635,7 +635,7 @@  discard block
 block discarded – undo
635 635
 		$this->connection->setConfiguration(['ldapCacheTTL' => $originalTTL]);
636 636
 		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
637 637
 		if (is_string($altName)) {
638
-			if($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
638
+			if ($this->mapAndAnnounceIfApplicable($mapper, $fdn, $altName, $uuid, $isUser)) {
639 639
 				$newlyMapped = true;
640 640
 				return $altName;
641 641
 			}
@@ -653,7 +653,7 @@  discard block
 block discarded – undo
653 653
 		string $uuid,
654 654
 		bool $isUser
655 655
 	) :bool {
656
-		if($mapper->map($fdn, $name, $uuid)) {
656
+		if ($mapper->map($fdn, $name, $uuid)) {
657 657
 			if ($this->ncUserManager instanceof PublicEmitter && $isUser) {
658 658
 				$this->cacheUserExists($name);
659 659
 				$this->ncUserManager->emit('\OC\User', 'assignedUserId', [$name]);
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
 	 * @throws \Exception
699 699
 	 */
700 700
 	private function ldap2NextcloudNames($ldapObjects, $isUsers) {
701
-		if($isUsers) {
701
+		if ($isUsers) {
702 702
 			$nameAttribute = $this->connection->ldapUserDisplayName;
703 703
 			$sndAttribute  = $this->connection->ldapUserDisplayName2;
704 704
 		} else {
@@ -706,9 +706,9 @@  discard block
 block discarded – undo
706 706
 		}
707 707
 		$nextcloudNames = [];
708 708
 
709
-		foreach($ldapObjects as $ldapObject) {
709
+		foreach ($ldapObjects as $ldapObject) {
710 710
 			$nameByLDAP = null;
711
-			if(    isset($ldapObject[$nameAttribute])
711
+			if (isset($ldapObject[$nameAttribute])
712 712
 				&& is_array($ldapObject[$nameAttribute])
713 713
 				&& isset($ldapObject[$nameAttribute][0])
714 714
 			) {
@@ -717,19 +717,19 @@  discard block
 block discarded – undo
717 717
 			}
718 718
 
719 719
 			$ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
720
-			if($ncName) {
720
+			if ($ncName) {
721 721
 				$nextcloudNames[] = $ncName;
722
-				if($isUsers) {
722
+				if ($isUsers) {
723 723
 					$this->updateUserState($ncName);
724 724
 					//cache the user names so it does not need to be retrieved
725 725
 					//again later (e.g. sharing dialogue).
726
-					if(is_null($nameByLDAP)) {
726
+					if (is_null($nameByLDAP)) {
727 727
 						continue;
728 728
 					}
729 729
 					$sndName = isset($ldapObject[$sndAttribute][0])
730 730
 						? $ldapObject[$sndAttribute][0] : '';
731 731
 					$this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
732
-				} else if($nameByLDAP !== null) {
732
+				} else if ($nameByLDAP !== null) {
733 733
 					$this->cacheGroupDisplayName($ncName, $nameByLDAP);
734 734
 				}
735 735
 			}
@@ -745,7 +745,7 @@  discard block
 block discarded – undo
745 745
 	 */
746 746
 	public function updateUserState($ncname) {
747 747
 		$user = $this->userManager->get($ncname);
748
-		if($user instanceof OfflineUser) {
748
+		if ($user instanceof OfflineUser) {
749 749
 			$user->unmark();
750 750
 		}
751 751
 	}
@@ -785,7 +785,7 @@  discard block
 block discarded – undo
785 785
 	 */
786 786
 	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
787 787
 		$user = $this->userManager->get($ocName);
788
-		if($user === null) {
788
+		if ($user === null) {
789 789
 			return;
790 790
 		}
791 791
 		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
@@ -794,7 +794,7 @@  discard block
 block discarded – undo
794 794
 	}
795 795
 
796 796
 	public function cacheGroupDisplayName(string $ncName, string $displayName): void {
797
-		$cacheKey = 'group_getDisplayName' . $ncName;
797
+		$cacheKey = 'group_getDisplayName'.$ncName;
798 798
 		$this->connection->writeToCache($cacheKey, $displayName);
799 799
 	}
800 800
 
@@ -810,9 +810,9 @@  discard block
 block discarded – undo
810 810
 		$attempts = 0;
811 811
 		//while loop is just a precaution. If a name is not generated within
812 812
 		//20 attempts, something else is very wrong. Avoids infinite loop.
813
-		while($attempts < 20){
814
-			$altName = $name . '_' . rand(1000,9999);
815
-			if(!$this->ncUserManager->userExists($altName)) {
813
+		while ($attempts < 20) {
814
+			$altName = $name.'_'.rand(1000, 9999);
815
+			if (!$this->ncUserManager->userExists($altName)) {
816 816
 				return $altName;
817 817
 			}
818 818
 			$attempts++;
@@ -834,25 +834,25 @@  discard block
 block discarded – undo
834 834
 	 */
835 835
 	private function _createAltInternalOwnCloudNameForGroups($name) {
836 836
 		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
837
-		if(!$usedNames || count($usedNames) === 0) {
837
+		if (!$usedNames || count($usedNames) === 0) {
838 838
 			$lastNo = 1; //will become name_2
839 839
 		} else {
840 840
 			natsort($usedNames);
841 841
 			$lastName = array_pop($usedNames);
842
-			$lastNo = (int)substr($lastName, strrpos($lastName, '_') + 1);
842
+			$lastNo = (int) substr($lastName, strrpos($lastName, '_') + 1);
843 843
 		}
844
-		$altName = $name.'_'. (string)($lastNo+1);
844
+		$altName = $name.'_'.(string) ($lastNo + 1);
845 845
 		unset($usedNames);
846 846
 
847 847
 		$attempts = 1;
848
-		while($attempts < 21){
848
+		while ($attempts < 21) {
849 849
 			// Check to be really sure it is unique
850 850
 			// while loop is just a precaution. If a name is not generated within
851 851
 			// 20 attempts, something else is very wrong. Avoids infinite loop.
852
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
852
+			if (!\OC::$server->getGroupManager()->groupExists($altName)) {
853 853
 				return $altName;
854 854
 			}
855
-			$altName = $name . '_' . ($lastNo + $attempts);
855
+			$altName = $name.'_'.($lastNo + $attempts);
856 856
 			$attempts++;
857 857
 		}
858 858
 		return false;
@@ -867,7 +867,7 @@  discard block
 block discarded – undo
867 867
 	private function createAltInternalOwnCloudName($name, $isUser) {
868 868
 		$originalTTL = $this->connection->ldapCacheTTL;
869 869
 		$this->connection->setConfiguration(['ldapCacheTTL' => 0]);
870
-		if($isUser) {
870
+		if ($isUser) {
871 871
 			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
872 872
 		} else {
873 873
 			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
@@ -916,13 +916,13 @@  discard block
 block discarded – undo
916 916
 	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
917 917
 		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
918 918
 		$recordsToUpdate = $ldapRecords;
919
-		if(!$forceApplyAttributes) {
919
+		if (!$forceApplyAttributes) {
920 920
 			$isBackgroundJobModeAjax = $this->config
921 921
 					->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
922 922
 			$recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
923 923
 				$newlyMapped = false;
924 924
 				$uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
925
-				if(is_string($uid)) {
925
+				if (is_string($uid)) {
926 926
 					$this->cacheUserExists($uid);
927 927
 				}
928 928
 				return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
@@ -940,15 +940,15 @@  discard block
 block discarded – undo
940 940
 	 * @param array $ldapRecords
941 941
 	 * @throws \Exception
942 942
 	 */
943
-	public function batchApplyUserAttributes(array $ldapRecords){
943
+	public function batchApplyUserAttributes(array $ldapRecords) {
944 944
 		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
945
-		foreach($ldapRecords as $userRecord) {
946
-			if(!isset($userRecord[$displayNameAttribute])) {
945
+		foreach ($ldapRecords as $userRecord) {
946
+			if (!isset($userRecord[$displayNameAttribute])) {
947 947
 				// displayName is obligatory
948 948
 				continue;
949 949
 			}
950
-			$ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
951
-			if($ocName === false) {
950
+			$ocName = $this->dn2ocname($userRecord['dn'][0], null, true);
951
+			if ($ocName === false) {
952 952
 				continue;
953 953
 			}
954 954
 			$this->updateUserState($ocName);
@@ -976,7 +976,7 @@  discard block
 block discarded – undo
976 976
 		array_walk($groupRecords, function($record) {
977 977
 			$newlyMapped = false;
978 978
 			$gid = $this->dn2ocname($record['dn'][0], null, false, $newlyMapped, $record);
979
-			if(!$newlyMapped && is_string($gid)) {
979
+			if (!$newlyMapped && is_string($gid)) {
980 980
 				$this->cacheGroupExists($gid);
981 981
 			}
982 982
 		});
@@ -989,8 +989,8 @@  discard block
 block discarded – undo
989 989
 	 * @return array
990 990
 	 */
991 991
 	private function fetchList($list, $manyAttributes) {
992
-		if(is_array($list)) {
993
-			if($manyAttributes) {
992
+		if (is_array($list)) {
993
+			if ($manyAttributes) {
994 994
 				return $list;
995 995
 			} else {
996 996
 				$list = array_reduce($list, function($carry, $item) {
@@ -1020,7 +1020,7 @@  discard block
 block discarded – undo
1020 1020
 	 */
1021 1021
 	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
1022 1022
 		$result = [];
1023
-		foreach($this->connection->ldapBaseUsers as $base) {
1023
+		foreach ($this->connection->ldapBaseUsers as $base) {
1024 1024
 			$result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1025 1025
 		}
1026 1026
 		return $result;
@@ -1036,9 +1036,9 @@  discard block
 block discarded – undo
1036 1036
 	 */
1037 1037
 	public function countUsers($filter, $attr = ['dn'], $limit = null, $offset = null) {
1038 1038
 		$result = false;
1039
-		foreach($this->connection->ldapBaseUsers as $base) {
1039
+		foreach ($this->connection->ldapBaseUsers as $base) {
1040 1040
 			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1041
-			$result = is_int($count) ? (int)$result + $count : $result;
1041
+			$result = is_int($count) ? (int) $result + $count : $result;
1042 1042
 		}
1043 1043
 		return $result;
1044 1044
 	}
@@ -1057,7 +1057,7 @@  discard block
 block discarded – undo
1057 1057
 	 */
1058 1058
 	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
1059 1059
 		$result = [];
1060
-		foreach($this->connection->ldapBaseGroups as $base) {
1060
+		foreach ($this->connection->ldapBaseGroups as $base) {
1061 1061
 			$result = array_merge($result, $this->search($filter, [$base], $attr, $limit, $offset));
1062 1062
 		}
1063 1063
 		return $result;
@@ -1075,9 +1075,9 @@  discard block
 block discarded – undo
1075 1075
 	 */
1076 1076
 	public function countGroups($filter, $attr = ['dn'], $limit = null, $offset = null) {
1077 1077
 		$result = false;
1078
-		foreach($this->connection->ldapBaseGroups as $base) {
1078
+		foreach ($this->connection->ldapBaseGroups as $base) {
1079 1079
 			$count = $this->count($filter, [$base], $attr, $limit, $offset);
1080
-			$result = is_int($count) ? (int)$result + $count : $result;
1080
+			$result = is_int($count) ? (int) $result + $count : $result;
1081 1081
 		}
1082 1082
 		return $result;
1083 1083
 	}
@@ -1092,9 +1092,9 @@  discard block
 block discarded – undo
1092 1092
 	 */
1093 1093
 	public function countObjects($limit = null, $offset = null) {
1094 1094
 		$result = false;
1095
-		foreach($this->connection->ldapBase as $base) {
1095
+		foreach ($this->connection->ldapBase as $base) {
1096 1096
 			$count = $this->count('objectclass=*', [$base], ['dn'], $limit, $offset);
1097
-			$result = is_int($count) ? (int)$result + $count : $result;
1097
+			$result = is_int($count) ? (int) $result + $count : $result;
1098 1098
 		}
1099 1099
 		return $result;
1100 1100
 	}
@@ -1119,7 +1119,7 @@  discard block
 block discarded – undo
1119 1119
 		// php no longer supports call-time pass-by-reference
1120 1120
 		// thus cannot support controlPagedResultResponse as the third argument
1121 1121
 		// is a reference
1122
-		$doMethod = function () use ($command, &$arguments) {
1122
+		$doMethod = function() use ($command, &$arguments) {
1123 1123
 			if ($command == 'controlPagedResultResponse') {
1124 1124
 				throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
1125 1125
 			} else {
@@ -1137,7 +1137,7 @@  discard block
 block discarded – undo
1137 1137
 			$this->connection->resetConnectionResource();
1138 1138
 			$cr = $this->connection->getConnectionResource();
1139 1139
 
1140
-			if(!$this->ldap->isResource($cr)) {
1140
+			if (!$this->ldap->isResource($cr)) {
1141 1141
 				// Seems like we didn't find any resource.
1142 1142
 				\OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", ILogger::DEBUG);
1143 1143
 				throw $e;
@@ -1162,13 +1162,13 @@  discard block
 block discarded – undo
1162 1162
 	 * @throws ServerNotAvailableException
1163 1163
 	 */
1164 1164
 	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1165
-		if(!is_null($attr) && !is_array($attr)) {
1165
+		if (!is_null($attr) && !is_array($attr)) {
1166 1166
 			$attr = [mb_strtolower($attr, 'UTF-8')];
1167 1167
 		}
1168 1168
 
1169 1169
 		// See if we have a resource, in case not cancel with message
1170 1170
 		$cr = $this->connection->getConnectionResource();
1171
-		if(!$this->ldap->isResource($cr)) {
1171
+		if (!$this->ldap->isResource($cr)) {
1172 1172
 			// Seems like we didn't find any resource.
1173 1173
 			// Return an empty array just like before.
1174 1174
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', ILogger::DEBUG);
@@ -1176,13 +1176,13 @@  discard block
 block discarded – undo
1176 1176
 		}
1177 1177
 
1178 1178
 		//check whether paged search should be attempted
1179
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, $offset);
1179
+		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int) $limit, $offset);
1180 1180
 
1181 1181
 		$linkResources = array_pad([], count($base), $cr);
1182 1182
 		$sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1183 1183
 		// cannot use $cr anymore, might have changed in the previous call!
1184 1184
 		$error = $this->ldap->errno($this->connection->getConnectionResource());
1185
-		if(!is_array($sr) || $error !== 0) {
1185
+		if (!is_array($sr) || $error !== 0) {
1186 1186
 			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), ILogger::ERROR);
1187 1187
 			return false;
1188 1188
 		}
@@ -1207,29 +1207,29 @@  discard block
 block discarded – undo
1207 1207
 	 */
1208 1208
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1209 1209
 		$cookie = null;
1210
-		if($pagedSearchOK) {
1210
+		if ($pagedSearchOK) {
1211 1211
 			$cr = $this->connection->getConnectionResource();
1212
-			foreach($sr as $key => $res) {
1213
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1212
+			foreach ($sr as $key => $res) {
1213
+				if ($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1214 1214
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1215 1215
 				}
1216 1216
 			}
1217 1217
 
1218 1218
 			//browsing through prior pages to get the cookie for the new one
1219
-			if($skipHandling) {
1219
+			if ($skipHandling) {
1220 1220
 				return false;
1221 1221
 			}
1222 1222
 			// if count is bigger, then the server does not support
1223 1223
 			// paged search. Instead, he did a normal search. We set a
1224 1224
 			// flag here, so the callee knows how to deal with it.
1225
-			if($iFoundItems <= $limit) {
1225
+			if ($iFoundItems <= $limit) {
1226 1226
 				$this->pagedSearchedSuccessful = true;
1227 1227
 			}
1228 1228
 		} else {
1229
-			if(!is_null($limit) && (int)$this->connection->ldapPagingSize !== 0) {
1229
+			if (!is_null($limit) && (int) $this->connection->ldapPagingSize !== 0) {
1230 1230
 				\OC::$server->getLogger()->debug(
1231 1231
 					'Paged search was not available',
1232
-					[ 'app' => 'user_ldap' ]
1232
+					['app' => 'user_ldap']
1233 1233
 				);
1234 1234
 			}
1235 1235
 		}
@@ -1258,8 +1258,8 @@  discard block
 block discarded – undo
1258 1258
 	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1259 1259
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), ILogger::DEBUG);
1260 1260
 
1261
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1262
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1261
+		$limitPerPage = (int) $this->connection->ldapPagingSize;
1262
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1263 1263
 			$limitPerPage = $limit;
1264 1264
 		}
1265 1265
 
@@ -1269,7 +1269,7 @@  discard block
 block discarded – undo
1269 1269
 
1270 1270
 		do {
1271 1271
 			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1272
-			if($search === false) {
1272
+			if ($search === false) {
1273 1273
 				return $counter > 0 ? $counter : false;
1274 1274
 			}
1275 1275
 			list($sr, $pagedSearchOK) = $search;
@@ -1288,7 +1288,7 @@  discard block
 block discarded – undo
1288 1288
 			 * Continue now depends on $hasMorePages value
1289 1289
 			 */
1290 1290
 			$continue = $pagedSearchOK && $hasMorePages;
1291
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1291
+		} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1292 1292
 
1293 1293
 		return $counter;
1294 1294
 	}
@@ -1301,8 +1301,8 @@  discard block
 block discarded – undo
1301 1301
 	private function countEntriesInSearchResults($searchResults) {
1302 1302
 		$counter = 0;
1303 1303
 
1304
-		foreach($searchResults as $res) {
1305
-			$count = (int)$this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res);
1304
+		foreach ($searchResults as $res) {
1305
+			$count = (int) $this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res);
1306 1306
 			$counter += $count;
1307 1307
 		}
1308 1308
 
@@ -1322,8 +1322,8 @@  discard block
 block discarded – undo
1322 1322
 	 * @throws ServerNotAvailableException
1323 1323
 	 */
1324 1324
 	public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1325
-		$limitPerPage = (int)$this->connection->ldapPagingSize;
1326
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1325
+		$limitPerPage = (int) $this->connection->ldapPagingSize;
1326
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1327 1327
 			$limitPerPage = $limit;
1328 1328
 		}
1329 1329
 
@@ -1337,13 +1337,13 @@  discard block
 block discarded – undo
1337 1337
 		$savedoffset = $offset;
1338 1338
 		do {
1339 1339
 			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1340
-			if($search === false) {
1340
+			if ($search === false) {
1341 1341
 				return [];
1342 1342
 			}
1343 1343
 			list($sr, $pagedSearchOK) = $search;
1344 1344
 			$cr = $this->connection->getConnectionResource();
1345 1345
 
1346
-			if($skipHandling) {
1346
+			if ($skipHandling) {
1347 1347
 				//i.e. result do not need to be fetched, we just need the cookie
1348 1348
 				//thus pass 1 or any other value as $iFoundItems because it is not
1349 1349
 				//used
@@ -1354,7 +1354,7 @@  discard block
 block discarded – undo
1354 1354
 			}
1355 1355
 
1356 1356
 			$iFoundItems = 0;
1357
-			foreach($sr as $res) {
1357
+			foreach ($sr as $res) {
1358 1358
 				$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1359 1359
 				$iFoundItems = max($iFoundItems, $findings['count']);
1360 1360
 				unset($findings['count']);
@@ -1370,27 +1370,27 @@  discard block
 block discarded – undo
1370 1370
 
1371 1371
 		// if we're here, probably no connection resource is returned.
1372 1372
 		// to make Nextcloud behave nicely, we simply give back an empty array.
1373
-		if(is_null($findings)) {
1373
+		if (is_null($findings)) {
1374 1374
 			return [];
1375 1375
 		}
1376 1376
 
1377
-		if(!is_null($attr)) {
1377
+		if (!is_null($attr)) {
1378 1378
 			$selection = [];
1379 1379
 			$i = 0;
1380
-			foreach($findings as $item) {
1381
-				if(!is_array($item)) {
1380
+			foreach ($findings as $item) {
1381
+				if (!is_array($item)) {
1382 1382
 					continue;
1383 1383
 				}
1384 1384
 				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1385
-				foreach($attr as $key) {
1386
-					if(isset($item[$key])) {
1387
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1385
+				foreach ($attr as $key) {
1386
+					if (isset($item[$key])) {
1387
+						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1388 1388
 							unset($item[$key]['count']);
1389 1389
 						}
1390
-						if($key !== 'dn') {
1391
-							if($this->resemblesDN($key)) {
1390
+						if ($key !== 'dn') {
1391
+							if ($this->resemblesDN($key)) {
1392 1392
 								$selection[$i][$key] = $this->helper->sanitizeDN($item[$key]);
1393
-							} else if($key === 'objectguid' || $key === 'guid') {
1393
+							} else if ($key === 'objectguid' || $key === 'guid') {
1394 1394
 								$selection[$i][$key] = [$this->convertObjectGUID2Str($item[$key][0])];
1395 1395
 							} else {
1396 1396
 								$selection[$i][$key] = $item[$key];
@@ -1408,14 +1408,14 @@  discard block
 block discarded – undo
1408 1408
 		//we slice the findings, when
1409 1409
 		//a) paged search unsuccessful, though attempted
1410 1410
 		//b) no paged search, but limit set
1411
-		if((!$this->getPagedSearchResultState()
1411
+		if ((!$this->getPagedSearchResultState()
1412 1412
 			&& $pagedSearchOK)
1413 1413
 			|| (
1414 1414
 				!$pagedSearchOK
1415 1415
 				&& !is_null($limit)
1416 1416
 			)
1417 1417
 		) {
1418
-			$findings = array_slice($findings, (int)$offset, $limit);
1418
+			$findings = array_slice($findings, (int) $offset, $limit);
1419 1419
 		}
1420 1420
 		return $findings;
1421 1421
 	}
@@ -1428,13 +1428,13 @@  discard block
 block discarded – undo
1428 1428
 	public function sanitizeUsername($name) {
1429 1429
 		$name = trim($name);
1430 1430
 
1431
-		if($this->connection->ldapIgnoreNamingRules) {
1431
+		if ($this->connection->ldapIgnoreNamingRules) {
1432 1432
 			return $name;
1433 1433
 		}
1434 1434
 
1435 1435
 		// Transliteration to ASCII
1436 1436
 		$transliterated = @iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1437
-		if($transliterated !== false) {
1437
+		if ($transliterated !== false) {
1438 1438
 			// depending on system config iconv can work or not
1439 1439
 			$name = $transliterated;
1440 1440
 		}
@@ -1445,7 +1445,7 @@  discard block
 block discarded – undo
1445 1445
 		// Every remaining disallowed characters will be removed
1446 1446
 		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1447 1447
 
1448
-		if($name === '') {
1448
+		if ($name === '') {
1449 1449
 			throw new \InvalidArgumentException('provided name template for username does not contain any allowed characters');
1450 1450
 		}
1451 1451
 
@@ -1460,13 +1460,13 @@  discard block
 block discarded – undo
1460 1460
 	 */
1461 1461
 	public function escapeFilterPart($input, $allowAsterisk = false) {
1462 1462
 		$asterisk = '';
1463
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1463
+		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1464 1464
 			$asterisk = '*';
1465 1465
 			$input = mb_substr($input, 1, null, 'UTF-8');
1466 1466
 		}
1467 1467
 		$search  = ['*', '\\', '(', ')'];
1468 1468
 		$replace = ['\\*', '\\\\', '\\(', '\\)'];
1469
-		return $asterisk . str_replace($search, $replace, $input);
1469
+		return $asterisk.str_replace($search, $replace, $input);
1470 1470
 	}
1471 1471
 
1472 1472
 	/**
@@ -1496,13 +1496,13 @@  discard block
 block discarded – undo
1496 1496
 	 */
1497 1497
 	private function combineFilter($filters, $operator) {
1498 1498
 		$combinedFilter = '('.$operator;
1499
-		foreach($filters as $filter) {
1499
+		foreach ($filters as $filter) {
1500 1500
 			if ($filter !== '' && $filter[0] !== '(') {
1501 1501
 				$filter = '('.$filter.')';
1502 1502
 			}
1503
-			$combinedFilter.=$filter;
1503
+			$combinedFilter .= $filter;
1504 1504
 		}
1505
-		$combinedFilter.=')';
1505
+		$combinedFilter .= ')';
1506 1506
 		return $combinedFilter;
1507 1507
 	}
1508 1508
 
@@ -1538,17 +1538,17 @@  discard block
 block discarded – undo
1538 1538
 	 * @throws \Exception
1539 1539
 	 */
1540 1540
 	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1541
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1541
+		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1542 1542
 			throw new \Exception('searchAttributes must be an array with at least two string');
1543 1543
 		}
1544 1544
 		$searchWords = explode(' ', trim($search));
1545 1545
 		$wordFilters = [];
1546
-		foreach($searchWords as $word) {
1546
+		foreach ($searchWords as $word) {
1547 1547
 			$word = $this->prepareSearchTerm($word);
1548 1548
 			//every word needs to appear at least once
1549 1549
 			$wordMatchOneAttrFilters = [];
1550
-			foreach($searchAttributes as $attr) {
1551
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1550
+			foreach ($searchAttributes as $attr) {
1551
+				$wordMatchOneAttrFilters[] = $attr.'='.$word;
1552 1552
 			}
1553 1553
 			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1554 1554
 		}
@@ -1566,10 +1566,10 @@  discard block
 block discarded – undo
1566 1566
 	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1567 1567
 		$filter = [];
1568 1568
 		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1569
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1569
+		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1570 1570
 			try {
1571 1571
 				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1572
-			} catch(\Exception $e) {
1572
+			} catch (\Exception $e) {
1573 1573
 				\OCP\Util::writeLog(
1574 1574
 					'user_ldap',
1575 1575
 					'Creating advanced filter for search failed, falling back to simple method.',
@@ -1579,17 +1579,17 @@  discard block
 block discarded – undo
1579 1579
 		}
1580 1580
 
1581 1581
 		$search = $this->prepareSearchTerm($search);
1582
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1582
+		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1583 1583
 			if ($fallbackAttribute === '') {
1584 1584
 				return '';
1585 1585
 			}
1586
-			$filter[] = $fallbackAttribute . '=' . $search;
1586
+			$filter[] = $fallbackAttribute.'='.$search;
1587 1587
 		} else {
1588
-			foreach($searchAttributes as $attribute) {
1589
-				$filter[] = $attribute . '=' . $search;
1588
+			foreach ($searchAttributes as $attribute) {
1589
+				$filter[] = $attribute.'='.$search;
1590 1590
 			}
1591 1591
 		}
1592
-		if(count($filter) === 1) {
1592
+		if (count($filter) === 1) {
1593 1593
 			return '('.$filter[0].')';
1594 1594
 		}
1595 1595
 		return $this->combineFilterWithOr($filter);
@@ -1610,7 +1610,7 @@  discard block
 block discarded – undo
1610 1610
 		if ($term === '') {
1611 1611
 			$result = '*';
1612 1612
 		} else if ($allowEnum !== 'no') {
1613
-			$result = $term . '*';
1613
+			$result = $term.'*';
1614 1614
 		}
1615 1615
 		return $result;
1616 1616
 	}
@@ -1622,7 +1622,7 @@  discard block
 block discarded – undo
1622 1622
 	public function getFilterForUserCount() {
1623 1623
 		$filter = $this->combineFilterWithAnd([
1624 1624
 			$this->connection->ldapUserFilter,
1625
-			$this->connection->ldapUserDisplayName . '=*'
1625
+			$this->connection->ldapUserDisplayName.'=*'
1626 1626
 		]);
1627 1627
 
1628 1628
 		return $filter;
@@ -1640,7 +1640,7 @@  discard block
 block discarded – undo
1640 1640
 			'ldapAgentName' => $name,
1641 1641
 			'ldapAgentPassword' => $password
1642 1642
 		];
1643
-		if(!$testConnection->setConfiguration($credentials)) {
1643
+		if (!$testConnection->setConfiguration($credentials)) {
1644 1644
 			return false;
1645 1645
 		}
1646 1646
 		return $testConnection->bind();
@@ -1662,30 +1662,30 @@  discard block
 block discarded – undo
1662 1662
 			// Sacrebleu! The UUID attribute is unknown :( We need first an
1663 1663
 			// existing DN to be able to reliably detect it.
1664 1664
 			$result = $this->search($filter, $base, ['dn'], 1);
1665
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1665
+			if (!isset($result[0]) || !isset($result[0]['dn'])) {
1666 1666
 				throw new \Exception('Cannot determine UUID attribute');
1667 1667
 			}
1668 1668
 			$dn = $result[0]['dn'][0];
1669
-			if(!$this->detectUuidAttribute($dn, true)) {
1669
+			if (!$this->detectUuidAttribute($dn, true)) {
1670 1670
 				throw new \Exception('Cannot determine UUID attribute');
1671 1671
 			}
1672 1672
 		} else {
1673 1673
 			// The UUID attribute is either known or an override is given.
1674 1674
 			// By calling this method we ensure that $this->connection->$uuidAttr
1675 1675
 			// is definitely set
1676
-			if(!$this->detectUuidAttribute('', true)) {
1676
+			if (!$this->detectUuidAttribute('', true)) {
1677 1677
 				throw new \Exception('Cannot determine UUID attribute');
1678 1678
 			}
1679 1679
 		}
1680 1680
 
1681 1681
 		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1682
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1682
+		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1683 1683
 			$uuid = $this->formatGuid2ForFilterUser($uuid);
1684 1684
 		}
1685 1685
 
1686
-		$filter = $uuidAttr . '=' . $uuid;
1686
+		$filter = $uuidAttr.'='.$uuid;
1687 1687
 		$result = $this->searchUsers($filter, ['dn'], 2);
1688
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1688
+		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1689 1689
 			// we put the count into account to make sure that this is
1690 1690
 			// really unique
1691 1691
 			return $result[0]['dn'][0];
@@ -1705,7 +1705,7 @@  discard block
 block discarded – undo
1705 1705
 	 * @throws ServerNotAvailableException
1706 1706
 	 */
1707 1707
 	private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1708
-		if($isUser) {
1708
+		if ($isUser) {
1709 1709
 			$uuidAttr     = 'ldapUuidUserAttribute';
1710 1710
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1711 1711
 		} else {
@@ -1713,8 +1713,8 @@  discard block
 block discarded – undo
1713 1713
 			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1714 1714
 		}
1715 1715
 
1716
-		if(!$force) {
1717
-			if($this->connection->$uuidAttr !== 'auto') {
1716
+		if (!$force) {
1717
+			if ($this->connection->$uuidAttr !== 'auto') {
1718 1718
 				return true;
1719 1719
 			} else if (is_string($uuidOverride) && trim($uuidOverride) !== '') {
1720 1720
 				$this->connection->$uuidAttr = $uuidOverride;
@@ -1722,23 +1722,23 @@  discard block
 block discarded – undo
1722 1722
 			}
1723 1723
 
1724 1724
 			$attribute = $this->connection->getFromCache($uuidAttr);
1725
-			if(!$attribute === null) {
1725
+			if (!$attribute === null) {
1726 1726
 				$this->connection->$uuidAttr = $attribute;
1727 1727
 				return true;
1728 1728
 			}
1729 1729
 		}
1730 1730
 
1731
-		foreach(self::UUID_ATTRIBUTES as $attribute) {
1732
-			if($ldapRecord !== null) {
1731
+		foreach (self::UUID_ATTRIBUTES as $attribute) {
1732
+			if ($ldapRecord !== null) {
1733 1733
 				// we have the info from LDAP already, we don't need to talk to the server again
1734
-				if(isset($ldapRecord[$attribute])) {
1734
+				if (isset($ldapRecord[$attribute])) {
1735 1735
 					$this->connection->$uuidAttr = $attribute;
1736 1736
 					return true;
1737 1737
 				}
1738 1738
 			}
1739 1739
 
1740 1740
 			$value = $this->readAttribute($dn, $attribute);
1741
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1741
+			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1742 1742
 				\OC::$server->getLogger()->debug(
1743 1743
 					'Setting {attribute} as {subject}',
1744 1744
 					[
@@ -1765,7 +1765,7 @@  discard block
 block discarded – undo
1765 1765
 	 * @throws ServerNotAvailableException
1766 1766
 	 */
1767 1767
 	public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1768
-		if($isUser) {
1768
+		if ($isUser) {
1769 1769
 			$uuidAttr     = 'ldapUuidUserAttribute';
1770 1770
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1771 1771
 		} else {
@@ -1774,10 +1774,10 @@  discard block
 block discarded – undo
1774 1774
 		}
1775 1775
 
1776 1776
 		$uuid = false;
1777
-		if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1777
+		if ($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1778 1778
 			$attr = $this->connection->$uuidAttr;
1779 1779
 			$uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1780
-			if( !is_array($uuid)
1780
+			if (!is_array($uuid)
1781 1781
 				&& $uuidOverride !== ''
1782 1782
 				&& $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1783 1783
 			{
@@ -1785,7 +1785,7 @@  discard block
 block discarded – undo
1785 1785
 					? $ldapRecord[$this->connection->$uuidAttr]
1786 1786
 					: $this->readAttribute($dn, $this->connection->$uuidAttr);
1787 1787
 			}
1788
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1788
+			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1789 1789
 				$uuid = $uuid[0];
1790 1790
 			}
1791 1791
 		}
@@ -1802,19 +1802,19 @@  discard block
 block discarded – undo
1802 1802
 	private function convertObjectGUID2Str($oguid) {
1803 1803
 		$hex_guid = bin2hex($oguid);
1804 1804
 		$hex_guid_to_guid_str = '';
1805
-		for($k = 1; $k <= 4; ++$k) {
1805
+		for ($k = 1; $k <= 4; ++$k) {
1806 1806
 			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1807 1807
 		}
1808 1808
 		$hex_guid_to_guid_str .= '-';
1809
-		for($k = 1; $k <= 2; ++$k) {
1809
+		for ($k = 1; $k <= 2; ++$k) {
1810 1810
 			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1811 1811
 		}
1812 1812
 		$hex_guid_to_guid_str .= '-';
1813
-		for($k = 1; $k <= 2; ++$k) {
1813
+		for ($k = 1; $k <= 2; ++$k) {
1814 1814
 			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1815 1815
 		}
1816
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1817
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1816
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);
1817
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);
1818 1818
 
1819 1819
 		return strtoupper($hex_guid_to_guid_str);
1820 1820
 	}
@@ -1831,11 +1831,11 @@  discard block
 block discarded – undo
1831 1831
 	 * @return string
1832 1832
 	 */
1833 1833
 	public function formatGuid2ForFilterUser($guid) {
1834
-		if(!is_string($guid)) {
1834
+		if (!is_string($guid)) {
1835 1835
 			throw new \InvalidArgumentException('String expected');
1836 1836
 		}
1837 1837
 		$blocks = explode('-', $guid);
1838
-		if(count($blocks) !== 5) {
1838
+		if (count($blocks) !== 5) {
1839 1839
 			/*
1840 1840
 			 * Why not throw an Exception instead? This method is a utility
1841 1841
 			 * called only when trying to figure out whether a "missing" known
@@ -1848,20 +1848,20 @@  discard block
 block discarded – undo
1848 1848
 			 * user. Instead we write a log message.
1849 1849
 			 */
1850 1850
 			\OC::$server->getLogger()->info(
1851
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1851
+				'Passed string does not resemble a valid GUID. Known UUID '.
1852 1852
 				'({uuid}) probably does not match UUID configuration.',
1853
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1853
+				['app' => 'user_ldap', 'uuid' => $guid]
1854 1854
 			);
1855 1855
 			return $guid;
1856 1856
 		}
1857
-		for($i=0; $i < 3; $i++) {
1857
+		for ($i = 0; $i < 3; $i++) {
1858 1858
 			$pairs = str_split($blocks[$i], 2);
1859 1859
 			$pairs = array_reverse($pairs);
1860 1860
 			$blocks[$i] = implode('', $pairs);
1861 1861
 		}
1862
-		for($i=0; $i < 5; $i++) {
1862
+		for ($i = 0; $i < 5; $i++) {
1863 1863
 			$pairs = str_split($blocks[$i], 2);
1864
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1864
+			$blocks[$i] = '\\'.implode('\\', $pairs);
1865 1865
 		}
1866 1866
 		return implode('', $blocks);
1867 1867
 	}
@@ -1877,12 +1877,12 @@  discard block
 block discarded – undo
1877 1877
 		$domainDN = $this->getDomainDNFromDN($dn);
1878 1878
 		$cacheKey = 'getSID-'.$domainDN;
1879 1879
 		$sid = $this->connection->getFromCache($cacheKey);
1880
-		if(!is_null($sid)) {
1880
+		if (!is_null($sid)) {
1881 1881
 			return $sid;
1882 1882
 		}
1883 1883
 
1884 1884
 		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1885
-		if(!is_array($objectSid) || empty($objectSid)) {
1885
+		if (!is_array($objectSid) || empty($objectSid)) {
1886 1886
 			$this->connection->writeToCache($cacheKey, false);
1887 1887
 			return false;
1888 1888
 		}
@@ -1940,12 +1940,12 @@  discard block
 block discarded – undo
1940 1940
 		$belongsToBase = false;
1941 1941
 		$bases = $this->helper->sanitizeDN($bases);
1942 1942
 
1943
-		foreach($bases as $base) {
1943
+		foreach ($bases as $base) {
1944 1944
 			$belongsToBase = true;
1945
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1945
+			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1946 1946
 				$belongsToBase = false;
1947 1947
 			}
1948
-			if($belongsToBase) {
1948
+			if ($belongsToBase) {
1949 1949
 				break;
1950 1950
 			}
1951 1951
 		}
@@ -1974,16 +1974,16 @@  discard block
 block discarded – undo
1974 1974
 	 * @return string containing the key or empty if none is cached
1975 1975
 	 */
1976 1976
 	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1977
-		if($offset === 0) {
1977
+		if ($offset === 0) {
1978 1978
 			return '';
1979 1979
 		}
1980 1980
 		$offset -= $limit;
1981 1981
 		//we work with cache here
1982
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
1982
+		$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.(int) $limit.'-'.(int) $offset;
1983 1983
 		$cookie = '';
1984
-		if(isset($this->cookies[$cacheKey])) {
1984
+		if (isset($this->cookies[$cacheKey])) {
1985 1985
 			$cookie = $this->cookies[$cacheKey];
1986
-			if(is_null($cookie)) {
1986
+			if (is_null($cookie)) {
1987 1987
 				$cookie = '';
1988 1988
 			}
1989 1989
 		}
@@ -2001,7 +2001,7 @@  discard block
 block discarded – undo
2001 2001
 	 * @return bool
2002 2002
 	 */
2003 2003
 	public function hasMoreResults() {
2004
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
2004
+		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
2005 2005
 			// as in RFC 2696, when all results are returned, the cookie will
2006 2006
 			// be empty.
2007 2007
 			return false;
@@ -2021,8 +2021,8 @@  discard block
 block discarded – undo
2021 2021
 	 */
2022 2022
 	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
2023 2023
 		// allow '0' for 389ds
2024
-		if(!empty($cookie) || $cookie === '0') {
2025
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . (int)$limit . '-' . (int)$offset;
2024
+		if (!empty($cookie) || $cookie === '0') {
2025
+			$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.(int) $limit.'-'.(int) $offset;
2026 2026
 			$this->cookies[$cacheKey] = $cookie;
2027 2027
 			$this->lastCookie = $cookie;
2028 2028
 		}
@@ -2052,16 +2052,16 @@  discard block
 block discarded – undo
2052 2052
 	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
2053 2053
 		$pagedSearchOK = false;
2054 2054
 		if ($limit !== 0) {
2055
-			$offset = (int)$offset; //can be null
2055
+			$offset = (int) $offset; //can be null
2056 2056
 			\OCP\Util::writeLog('user_ldap',
2057 2057
 				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
2058
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
2058
+				.' attr '.print_r($attr, true).' limit '.$limit.' offset '.$offset,
2059 2059
 				ILogger::DEBUG);
2060 2060
 			//get the cookie from the search for the previous search, required by LDAP
2061
-			foreach($bases as $base) {
2061
+			foreach ($bases as $base) {
2062 2062
 
2063 2063
 				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
2064
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
2064
+				if (empty($cookie) && $cookie !== "0" && ($offset > 0)) {
2065 2065
 					// no cookie known from a potential previous search. We need
2066 2066
 					// to start from 0 to come to the desired page. cookie value
2067 2067
 					// of '0' is valid, because 389ds
@@ -2071,17 +2071,17 @@  discard block
 block discarded – undo
2071 2071
 					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
2072 2072
 					// '0' is valid, because 389ds
2073 2073
 					//TODO: remember this, probably does not change in the next request...
2074
-					if(empty($cookie) && $cookie !== '0') {
2074
+					if (empty($cookie) && $cookie !== '0') {
2075 2075
 						$cookie = null;
2076 2076
 					}
2077 2077
 				}
2078
-				if(!is_null($cookie)) {
2078
+				if (!is_null($cookie)) {
2079 2079
 					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
2080 2080
 					$this->abandonPagedSearch();
2081 2081
 					$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2082 2082
 						$this->connection->getConnectionResource(), $limit,
2083 2083
 						false, $cookie);
2084
-					if(!$pagedSearchOK) {
2084
+					if (!$pagedSearchOK) {
2085 2085
 						return false;
2086 2086
 					}
2087 2087
 					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', ILogger::DEBUG);
@@ -2104,7 +2104,7 @@  discard block
 block discarded – undo
2104 2104
 			$this->abandonPagedSearch();
2105 2105
 			// in case someone set it to 0 … use 500, otherwise no results will
2106 2106
 			// be returned.
2107
-			$pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
2107
+			$pageSize = (int) $this->connection->ldapPagingSize > 0 ? (int) $this->connection->ldapPagingSize : 500;
2108 2108
 			$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
2109 2109
 				$this->connection->getConnectionResource(),
2110 2110
 				$pageSize, false, '');
Please login to merge, or discard this patch.
apps/user_ldap/lib/User_LDAP.php 2 patches
Indentation   +579 added lines, -579 removed lines patch added patch discarded remove patch
@@ -53,587 +53,587 @@
 block discarded – undo
53 53
 use OCP\Util;
54 54
 
55 55
 class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP {
56
-	/** @var \OCP\IConfig */
57
-	protected $ocConfig;
58
-
59
-	/** @var INotificationManager */
60
-	protected $notificationManager;
61
-
62
-	/** @var UserPluginManager */
63
-	protected $userPluginManager;
64
-
65
-	/**
66
-	 * @param Access $access
67
-	 * @param \OCP\IConfig $ocConfig
68
-	 * @param \OCP\Notification\IManager $notificationManager
69
-	 * @param IUserSession $userSession
70
-	 */
71
-	public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager, IUserSession $userSession, UserPluginManager $userPluginManager) {
72
-		parent::__construct($access);
73
-		$this->ocConfig = $ocConfig;
74
-		$this->notificationManager = $notificationManager;
75
-		$this->userPluginManager = $userPluginManager;
76
-	}
77
-
78
-	/**
79
-	 * checks whether the user is allowed to change his avatar in Nextcloud
80
-	 *
81
-	 * @param string $uid the Nextcloud user name
82
-	 * @return boolean either the user can or cannot
83
-	 * @throws \Exception
84
-	 */
85
-	public function canChangeAvatar($uid) {
86
-		if ($this->userPluginManager->implementsActions(Backend::PROVIDE_AVATAR)) {
87
-			return $this->userPluginManager->canChangeAvatar($uid);
88
-		}
89
-
90
-		if(!$this->implementsActions(Backend::PROVIDE_AVATAR)) {
91
-			return true;
92
-		}
93
-
94
-		$user = $this->access->userManager->get($uid);
95
-		if(!$user instanceof User) {
96
-			return false;
97
-		}
98
-		$imageData = $user->getAvatarImage();
99
-		if($imageData === false) {
100
-			return true;
101
-		}
102
-		return !$user->updateAvatar(true);
103
-	}
104
-
105
-	/**
106
-	 * Return the username for the given login name, if available
107
-	 *
108
-	 * @param string $loginName
109
-	 * @return string|false
110
-	 * @throws \Exception
111
-	 */
112
-	public function loginName2UserName($loginName) {
113
-		$cacheKey = 'loginName2UserName-' . $loginName;
114
-		$username = $this->access->connection->getFromCache($cacheKey);
115
-
116
-		if ($username !== null) {
117
-			return $username;
118
-		}
119
-
120
-		try {
121
-			$ldapRecord = $this->getLDAPUserByLoginName($loginName);
122
-			$user = $this->access->userManager->get($ldapRecord['dn'][0]);
123
-			if ($user === null || $user instanceof OfflineUser) {
124
-				// this path is not really possible, however get() is documented
125
-				// to return User, OfflineUser or null so we are very defensive here.
126
-				$this->access->connection->writeToCache($cacheKey, false);
127
-				return false;
128
-			}
129
-			$username = $user->getUsername();
130
-			$this->access->connection->writeToCache($cacheKey, $username);
131
-			return $username;
132
-		} catch (NotOnLDAP $e) {
133
-			$this->access->connection->writeToCache($cacheKey, false);
134
-			return false;
135
-		}
136
-	}
56
+    /** @var \OCP\IConfig */
57
+    protected $ocConfig;
58
+
59
+    /** @var INotificationManager */
60
+    protected $notificationManager;
61
+
62
+    /** @var UserPluginManager */
63
+    protected $userPluginManager;
64
+
65
+    /**
66
+     * @param Access $access
67
+     * @param \OCP\IConfig $ocConfig
68
+     * @param \OCP\Notification\IManager $notificationManager
69
+     * @param IUserSession $userSession
70
+     */
71
+    public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager, IUserSession $userSession, UserPluginManager $userPluginManager) {
72
+        parent::__construct($access);
73
+        $this->ocConfig = $ocConfig;
74
+        $this->notificationManager = $notificationManager;
75
+        $this->userPluginManager = $userPluginManager;
76
+    }
77
+
78
+    /**
79
+     * checks whether the user is allowed to change his avatar in Nextcloud
80
+     *
81
+     * @param string $uid the Nextcloud user name
82
+     * @return boolean either the user can or cannot
83
+     * @throws \Exception
84
+     */
85
+    public function canChangeAvatar($uid) {
86
+        if ($this->userPluginManager->implementsActions(Backend::PROVIDE_AVATAR)) {
87
+            return $this->userPluginManager->canChangeAvatar($uid);
88
+        }
89
+
90
+        if(!$this->implementsActions(Backend::PROVIDE_AVATAR)) {
91
+            return true;
92
+        }
93
+
94
+        $user = $this->access->userManager->get($uid);
95
+        if(!$user instanceof User) {
96
+            return false;
97
+        }
98
+        $imageData = $user->getAvatarImage();
99
+        if($imageData === false) {
100
+            return true;
101
+        }
102
+        return !$user->updateAvatar(true);
103
+    }
104
+
105
+    /**
106
+     * Return the username for the given login name, if available
107
+     *
108
+     * @param string $loginName
109
+     * @return string|false
110
+     * @throws \Exception
111
+     */
112
+    public function loginName2UserName($loginName) {
113
+        $cacheKey = 'loginName2UserName-' . $loginName;
114
+        $username = $this->access->connection->getFromCache($cacheKey);
115
+
116
+        if ($username !== null) {
117
+            return $username;
118
+        }
119
+
120
+        try {
121
+            $ldapRecord = $this->getLDAPUserByLoginName($loginName);
122
+            $user = $this->access->userManager->get($ldapRecord['dn'][0]);
123
+            if ($user === null || $user instanceof OfflineUser) {
124
+                // this path is not really possible, however get() is documented
125
+                // to return User, OfflineUser or null so we are very defensive here.
126
+                $this->access->connection->writeToCache($cacheKey, false);
127
+                return false;
128
+            }
129
+            $username = $user->getUsername();
130
+            $this->access->connection->writeToCache($cacheKey, $username);
131
+            return $username;
132
+        } catch (NotOnLDAP $e) {
133
+            $this->access->connection->writeToCache($cacheKey, false);
134
+            return false;
135
+        }
136
+    }
137 137
 	
138
-	/**
139
-	 * returns the username for the given LDAP DN, if available
140
-	 *
141
-	 * @param string $dn
142
-	 * @return string|false with the username
143
-	 */
144
-	public function dn2UserName($dn) {
145
-		return $this->access->dn2username($dn);
146
-	}
147
-
148
-	/**
149
-	 * returns an LDAP record based on a given login name
150
-	 *
151
-	 * @param string $loginName
152
-	 * @return array
153
-	 * @throws NotOnLDAP
154
-	 */
155
-	public function getLDAPUserByLoginName($loginName) {
156
-		//find out dn of the user name
157
-		$attrs = $this->access->userManager->getAttributes();
158
-		$users = $this->access->fetchUsersByLoginName($loginName, $attrs);
159
-		if(count($users) < 1) {
160
-			throw new NotOnLDAP('No user available for the given login name on ' .
161
-				$this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
162
-		}
163
-		return $users[0];
164
-	}
165
-
166
-	/**
167
-	 * Check if the password is correct without logging in the user
168
-	 *
169
-	 * @param string $uid The username
170
-	 * @param string $password The password
171
-	 * @return false|string
172
-	 */
173
-	public function checkPassword($uid, $password) {
174
-		try {
175
-			$ldapRecord = $this->getLDAPUserByLoginName($uid);
176
-		} catch(NotOnLDAP $e) {
177
-			\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap', 'level' => ILogger::DEBUG]);
178
-			return false;
179
-		}
180
-		$dn = $ldapRecord['dn'][0];
181
-		$user = $this->access->userManager->get($dn);
182
-
183
-		if(!$user instanceof User) {
184
-			Util::writeLog('user_ldap',
185
-				'LDAP Login: Could not get user object for DN ' . $dn .
186
-				'. Maybe the LDAP entry has no set display name attribute?',
187
-				ILogger::WARN);
188
-			return false;
189
-		}
190
-		if($user->getUsername() !== false) {
191
-			//are the credentials OK?
192
-			if(!$this->access->areCredentialsValid($dn, $password)) {
193
-				return false;
194
-			}
195
-
196
-			$this->access->cacheUserExists($user->getUsername());
197
-			$user->processAttributes($ldapRecord);
198
-			$user->markLogin();
199
-
200
-			return $user->getUsername();
201
-		}
202
-
203
-		return false;
204
-	}
205
-
206
-	/**
207
-	 * Set password
208
-	 * @param string $uid The username
209
-	 * @param string $password The new password
210
-	 * @return bool
211
-	 */
212
-	public function setPassword($uid, $password) {
213
-		if ($this->userPluginManager->implementsActions(Backend::SET_PASSWORD)) {
214
-			return $this->userPluginManager->setPassword($uid, $password);
215
-		}
216
-
217
-		$user = $this->access->userManager->get($uid);
218
-
219
-		if(!$user instanceof User) {
220
-			throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
221
-				'. Maybe the LDAP entry has no set display name attribute?');
222
-		}
223
-		if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
224
-			$ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
225
-			$turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
226
-			if (!empty($ldapDefaultPPolicyDN) && ((int)$turnOnPasswordChange === 1)) {
227
-				//remove last password expiry warning if any
228
-				$notification = $this->notificationManager->createNotification();
229
-				$notification->setApp('user_ldap')
230
-					->setUser($uid)
231
-					->setObject('pwd_exp_warn', $uid)
232
-				;
233
-				$this->notificationManager->markProcessed($notification);
234
-			}
235
-			return true;
236
-		}
237
-
238
-		return false;
239
-	}
240
-
241
-	/**
242
-	 * Get a list of all users
243
-	 *
244
-	 * @param string $search
245
-	 * @param integer $limit
246
-	 * @param integer $offset
247
-	 * @return string[] an array of all uids
248
-	 */
249
-	public function getUsers($search = '', $limit = 10, $offset = 0) {
250
-		$search = $this->access->escapeFilterPart($search, true);
251
-		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
252
-
253
-		//check if users are cached, if so return
254
-		$ldap_users = $this->access->connection->getFromCache($cachekey);
255
-		if(!is_null($ldap_users)) {
256
-			return $ldap_users;
257
-		}
258
-
259
-		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
260
-		// error. With a limit of 0, we get 0 results. So we pass null.
261
-		if($limit <= 0) {
262
-			$limit = null;
263
-		}
264
-		$filter = $this->access->combineFilterWithAnd([
265
-			$this->access->connection->ldapUserFilter,
266
-			$this->access->connection->ldapUserDisplayName . '=*',
267
-			$this->access->getFilterPartForUserSearch($search)
268
-		]);
269
-
270
-		Util::writeLog('user_ldap',
271
-			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
272
-			ILogger::DEBUG);
273
-		//do the search and translate results to Nextcloud names
274
-		$ldap_users = $this->access->fetchListOfUsers(
275
-			$filter,
276
-			$this->access->userManager->getAttributes(true),
277
-			$limit, $offset);
278
-		$ldap_users = $this->access->nextcloudUserNames($ldap_users);
279
-		Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', ILogger::DEBUG);
280
-
281
-		$this->access->connection->writeToCache($cachekey, $ldap_users);
282
-		return $ldap_users;
283
-	}
284
-
285
-	/**
286
-	 * checks whether a user is still available on LDAP
287
-	 *
288
-	 * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user
289
-	 * name or an instance of that user
290
-	 * @return bool
291
-	 * @throws \Exception
292
-	 * @throws \OC\ServerNotAvailableException
293
-	 */
294
-	public function userExistsOnLDAP($user) {
295
-		if(is_string($user)) {
296
-			$user = $this->access->userManager->get($user);
297
-		}
298
-		if(is_null($user)) {
299
-			return false;
300
-		}
301
-		$uid = $user instanceof User ? $user->getUsername() : $user->getOCName();
302
-		$cacheKey = 'userExistsOnLDAP' . $uid;
303
-		$userExists = $this->access->connection->getFromCache($cacheKey);
304
-		if(!is_null($userExists)) {
305
-			return (bool)$userExists;
306
-		}
307
-
308
-		$dn = $user->getDN();
309
-		//check if user really still exists by reading its entry
310
-		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
311
-			try {
312
-				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
313
-				if (!$uuid) {
314
-					$this->access->connection->writeToCache($cacheKey, false);
315
-					return false;
316
-				}
317
-				$newDn = $this->access->getUserDnByUuid($uuid);
318
-				//check if renamed user is still valid by reapplying the ldap filter
319
-				if ($newDn === $dn || !is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
320
-					$this->access->connection->writeToCache($cacheKey, false);
321
-					return false;
322
-				}
323
-				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
324
-				$this->access->connection->writeToCache($cacheKey, true);
325
-				return true;
326
-			} catch (ServerNotAvailableException $e) {
327
-				throw $e;
328
-			} catch (\Exception $e) {
329
-				$this->access->connection->writeToCache($cacheKey, false);
330
-				return false;
331
-			}
332
-		}
333
-
334
-		if($user instanceof OfflineUser) {
335
-			$user->unmark();
336
-		}
337
-
338
-		$this->access->connection->writeToCache($cacheKey, true);
339
-		return true;
340
-	}
341
-
342
-	/**
343
-	 * check if a user exists
344
-	 * @param string $uid the username
345
-	 * @return boolean
346
-	 * @throws \Exception when connection could not be established
347
-	 */
348
-	public function userExists($uid) {
349
-		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
350
-		if(!is_null($userExists)) {
351
-			return (bool)$userExists;
352
-		}
353
-		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
354
-		$user = $this->access->userManager->get($uid);
355
-
356
-		if(is_null($user)) {
357
-			Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
358
-				$this->access->connection->ldapHost, ILogger::DEBUG);
359
-			$this->access->connection->writeToCache('userExists'.$uid, false);
360
-			return false;
361
-		}
362
-
363
-		$this->access->connection->writeToCache('userExists'.$uid, true);
364
-		return true;
365
-	}
366
-
367
-	/**
368
-	 * returns whether a user was deleted in LDAP
369
-	 *
370
-	 * @param string $uid The username of the user to delete
371
-	 * @return bool
372
-	 */
373
-	public function deleteUser($uid) {
374
-		if ($this->userPluginManager->canDeleteUser()) {
375
-			$status = $this->userPluginManager->deleteUser($uid);
376
-			if($status === false) {
377
-				return false;
378
-			}
379
-		}
380
-
381
-		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
382
-		if((int)$marked === 0) {
383
-			\OC::$server->getLogger()->notice(
384
-				'User '.$uid . ' is not marked as deleted, not cleaning up.',
385
-				['app' => 'user_ldap']);
386
-			return false;
387
-		}
388
-		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
389
-			['app' => 'user_ldap']);
390
-
391
-		$this->access->getUserMapper()->unmap($uid); // we don't emit unassign signals here, since it is implicit to delete signals fired from core
392
-		$this->access->userManager->invalidate($uid);
393
-		return true;
394
-	}
395
-
396
-	/**
397
-	 * get the user's home directory
398
-	 *
399
-	 * @param string $uid the username
400
-	 * @return bool|string
401
-	 * @throws NoUserException
402
-	 * @throws \Exception
403
-	 */
404
-	public function getHome($uid) {
405
-		// user Exists check required as it is not done in user proxy!
406
-		if(!$this->userExists($uid)) {
407
-			return false;
408
-		}
409
-
410
-		if ($this->userPluginManager->implementsActions(Backend::GET_HOME)) {
411
-			return $this->userPluginManager->getHome($uid);
412
-		}
413
-
414
-		$cacheKey = 'getHome'.$uid;
415
-		$path = $this->access->connection->getFromCache($cacheKey);
416
-		if(!is_null($path)) {
417
-			return $path;
418
-		}
419
-
420
-		// early return path if it is a deleted user
421
-		$user = $this->access->userManager->get($uid);
422
-		if($user instanceof User || $user instanceof OfflineUser) {
423
-			$path = $user->getHomePath() ?: false;
424
-		} else {
425
-			throw new NoUserException($uid . ' is not a valid user anymore');
426
-		}
427
-
428
-		$this->access->cacheUserHome($uid, $path);
429
-		return $path;
430
-	}
431
-
432
-	/**
433
-	 * get display name of the user
434
-	 * @param string $uid user ID of the user
435
-	 * @return string|false display name
436
-	 */
437
-	public function getDisplayName($uid) {
438
-		if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) {
439
-			return $this->userPluginManager->getDisplayName($uid);
440
-		}
441
-
442
-		if(!$this->userExists($uid)) {
443
-			return false;
444
-		}
445
-
446
-		$cacheKey = 'getDisplayName'.$uid;
447
-		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
448
-			return $displayName;
449
-		}
450
-
451
-		//Check whether the display name is configured to have a 2nd feature
452
-		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
453
-		$displayName2 = '';
454
-		if ($additionalAttribute !== '') {
455
-			$displayName2 = $this->access->readAttribute(
456
-				$this->access->username2dn($uid),
457
-				$additionalAttribute);
458
-		}
459
-
460
-		$displayName = $this->access->readAttribute(
461
-			$this->access->username2dn($uid),
462
-			$this->access->connection->ldapUserDisplayName);
463
-
464
-		if($displayName && (count($displayName) > 0)) {
465
-			$displayName = $displayName[0];
466
-
467
-			if (is_array($displayName2)){
468
-				$displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
469
-			}
470
-
471
-			$user = $this->access->userManager->get($uid);
472
-			if ($user instanceof User) {
473
-				$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
474
-				$this->access->connection->writeToCache($cacheKey, $displayName);
475
-			}
476
-			if ($user instanceof OfflineUser) {
477
-				/** @var OfflineUser $user*/
478
-				$displayName = $user->getDisplayName();
479
-			}
480
-			return $displayName;
481
-		}
482
-
483
-		return null;
484
-	}
485
-
486
-	/**
487
-	 * set display name of the user
488
-	 * @param string $uid user ID of the user
489
-	 * @param string $displayName new display name of the user
490
-	 * @return string|false display name
491
-	 */
492
-	public function setDisplayName($uid, $displayName) {
493
-		if ($this->userPluginManager->implementsActions(Backend::SET_DISPLAYNAME)) {
494
-			$this->userPluginManager->setDisplayName($uid, $displayName);
495
-			$this->access->cacheUserDisplayName($uid, $displayName);
496
-			return $displayName;
497
-		}
498
-		return false;
499
-	}
500
-
501
-	/**
502
-	 * Get a list of all display names
503
-	 *
504
-	 * @param string $search
505
-	 * @param string|null $limit
506
-	 * @param string|null $offset
507
-	 * @return array an array of all displayNames (value) and the corresponding uids (key)
508
-	 */
509
-	public function getDisplayNames($search = '', $limit = null, $offset = null) {
510
-		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
511
-		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
512
-			return $displayNames;
513
-		}
514
-
515
-		$displayNames = [];
516
-		$users = $this->getUsers($search, $limit, $offset);
517
-		foreach ($users as $user) {
518
-			$displayNames[$user] = $this->getDisplayName($user);
519
-		}
520
-		$this->access->connection->writeToCache($cacheKey, $displayNames);
521
-		return $displayNames;
522
-	}
523
-
524
-	/**
525
-	 * Check if backend implements actions
526
-	 * @param int $actions bitwise-or'ed actions
527
-	 * @return boolean
528
-	 *
529
-	 * Returns the supported actions as int to be
530
-	 * compared with \OC\User\Backend::CREATE_USER etc.
531
-	 */
532
-	public function implementsActions($actions) {
533
-		return (bool)((Backend::CHECK_PASSWORD
534
-			| Backend::GET_HOME
535
-			| Backend::GET_DISPLAYNAME
536
-			| (($this->access->connection->ldapUserAvatarRule !== 'none') ? Backend::PROVIDE_AVATAR : 0)
537
-			| Backend::COUNT_USERS
538
-			| (((int)$this->access->connection->turnOnPasswordChange === 1)? Backend::SET_PASSWORD :0)
539
-			| $this->userPluginManager->getImplementedActions())
540
-			& $actions);
541
-	}
542
-
543
-	/**
544
-	 * @return bool
545
-	 */
546
-	public function hasUserListings() {
547
-		return true;
548
-	}
549
-
550
-	/**
551
-	 * counts the users in LDAP
552
-	 *
553
-	 * @return int|bool
554
-	 */
555
-	public function countUsers() {
556
-		if ($this->userPluginManager->implementsActions(Backend::COUNT_USERS)) {
557
-			return $this->userPluginManager->countUsers();
558
-		}
559
-
560
-		$filter = $this->access->getFilterForUserCount();
561
-		$cacheKey = 'countUsers-'.$filter;
562
-		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
563
-			return $entries;
564
-		}
565
-		$entries = $this->access->countUsers($filter);
566
-		$this->access->connection->writeToCache($cacheKey, $entries);
567
-		return $entries;
568
-	}
569
-
570
-	/**
571
-	 * Backend name to be shown in user management
572
-	 * @return string the name of the backend to be shown
573
-	 */
574
-	public function getBackendName(){
575
-		return 'LDAP';
576
-	}
138
+    /**
139
+     * returns the username for the given LDAP DN, if available
140
+     *
141
+     * @param string $dn
142
+     * @return string|false with the username
143
+     */
144
+    public function dn2UserName($dn) {
145
+        return $this->access->dn2username($dn);
146
+    }
147
+
148
+    /**
149
+     * returns an LDAP record based on a given login name
150
+     *
151
+     * @param string $loginName
152
+     * @return array
153
+     * @throws NotOnLDAP
154
+     */
155
+    public function getLDAPUserByLoginName($loginName) {
156
+        //find out dn of the user name
157
+        $attrs = $this->access->userManager->getAttributes();
158
+        $users = $this->access->fetchUsersByLoginName($loginName, $attrs);
159
+        if(count($users) < 1) {
160
+            throw new NotOnLDAP('No user available for the given login name on ' .
161
+                $this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
162
+        }
163
+        return $users[0];
164
+    }
165
+
166
+    /**
167
+     * Check if the password is correct without logging in the user
168
+     *
169
+     * @param string $uid The username
170
+     * @param string $password The password
171
+     * @return false|string
172
+     */
173
+    public function checkPassword($uid, $password) {
174
+        try {
175
+            $ldapRecord = $this->getLDAPUserByLoginName($uid);
176
+        } catch(NotOnLDAP $e) {
177
+            \OC::$server->getLogger()->logException($e, ['app' => 'user_ldap', 'level' => ILogger::DEBUG]);
178
+            return false;
179
+        }
180
+        $dn = $ldapRecord['dn'][0];
181
+        $user = $this->access->userManager->get($dn);
182
+
183
+        if(!$user instanceof User) {
184
+            Util::writeLog('user_ldap',
185
+                'LDAP Login: Could not get user object for DN ' . $dn .
186
+                '. Maybe the LDAP entry has no set display name attribute?',
187
+                ILogger::WARN);
188
+            return false;
189
+        }
190
+        if($user->getUsername() !== false) {
191
+            //are the credentials OK?
192
+            if(!$this->access->areCredentialsValid($dn, $password)) {
193
+                return false;
194
+            }
195
+
196
+            $this->access->cacheUserExists($user->getUsername());
197
+            $user->processAttributes($ldapRecord);
198
+            $user->markLogin();
199
+
200
+            return $user->getUsername();
201
+        }
202
+
203
+        return false;
204
+    }
205
+
206
+    /**
207
+     * Set password
208
+     * @param string $uid The username
209
+     * @param string $password The new password
210
+     * @return bool
211
+     */
212
+    public function setPassword($uid, $password) {
213
+        if ($this->userPluginManager->implementsActions(Backend::SET_PASSWORD)) {
214
+            return $this->userPluginManager->setPassword($uid, $password);
215
+        }
216
+
217
+        $user = $this->access->userManager->get($uid);
218
+
219
+        if(!$user instanceof User) {
220
+            throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
221
+                '. Maybe the LDAP entry has no set display name attribute?');
222
+        }
223
+        if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
224
+            $ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
225
+            $turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
226
+            if (!empty($ldapDefaultPPolicyDN) && ((int)$turnOnPasswordChange === 1)) {
227
+                //remove last password expiry warning if any
228
+                $notification = $this->notificationManager->createNotification();
229
+                $notification->setApp('user_ldap')
230
+                    ->setUser($uid)
231
+                    ->setObject('pwd_exp_warn', $uid)
232
+                ;
233
+                $this->notificationManager->markProcessed($notification);
234
+            }
235
+            return true;
236
+        }
237
+
238
+        return false;
239
+    }
240
+
241
+    /**
242
+     * Get a list of all users
243
+     *
244
+     * @param string $search
245
+     * @param integer $limit
246
+     * @param integer $offset
247
+     * @return string[] an array of all uids
248
+     */
249
+    public function getUsers($search = '', $limit = 10, $offset = 0) {
250
+        $search = $this->access->escapeFilterPart($search, true);
251
+        $cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
252
+
253
+        //check if users are cached, if so return
254
+        $ldap_users = $this->access->connection->getFromCache($cachekey);
255
+        if(!is_null($ldap_users)) {
256
+            return $ldap_users;
257
+        }
258
+
259
+        // if we'd pass -1 to LDAP search, we'd end up in a Protocol
260
+        // error. With a limit of 0, we get 0 results. So we pass null.
261
+        if($limit <= 0) {
262
+            $limit = null;
263
+        }
264
+        $filter = $this->access->combineFilterWithAnd([
265
+            $this->access->connection->ldapUserFilter,
266
+            $this->access->connection->ldapUserDisplayName . '=*',
267
+            $this->access->getFilterPartForUserSearch($search)
268
+        ]);
269
+
270
+        Util::writeLog('user_ldap',
271
+            'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
272
+            ILogger::DEBUG);
273
+        //do the search and translate results to Nextcloud names
274
+        $ldap_users = $this->access->fetchListOfUsers(
275
+            $filter,
276
+            $this->access->userManager->getAttributes(true),
277
+            $limit, $offset);
278
+        $ldap_users = $this->access->nextcloudUserNames($ldap_users);
279
+        Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', ILogger::DEBUG);
280
+
281
+        $this->access->connection->writeToCache($cachekey, $ldap_users);
282
+        return $ldap_users;
283
+    }
284
+
285
+    /**
286
+     * checks whether a user is still available on LDAP
287
+     *
288
+     * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user
289
+     * name or an instance of that user
290
+     * @return bool
291
+     * @throws \Exception
292
+     * @throws \OC\ServerNotAvailableException
293
+     */
294
+    public function userExistsOnLDAP($user) {
295
+        if(is_string($user)) {
296
+            $user = $this->access->userManager->get($user);
297
+        }
298
+        if(is_null($user)) {
299
+            return false;
300
+        }
301
+        $uid = $user instanceof User ? $user->getUsername() : $user->getOCName();
302
+        $cacheKey = 'userExistsOnLDAP' . $uid;
303
+        $userExists = $this->access->connection->getFromCache($cacheKey);
304
+        if(!is_null($userExists)) {
305
+            return (bool)$userExists;
306
+        }
307
+
308
+        $dn = $user->getDN();
309
+        //check if user really still exists by reading its entry
310
+        if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
311
+            try {
312
+                $uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
313
+                if (!$uuid) {
314
+                    $this->access->connection->writeToCache($cacheKey, false);
315
+                    return false;
316
+                }
317
+                $newDn = $this->access->getUserDnByUuid($uuid);
318
+                //check if renamed user is still valid by reapplying the ldap filter
319
+                if ($newDn === $dn || !is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
320
+                    $this->access->connection->writeToCache($cacheKey, false);
321
+                    return false;
322
+                }
323
+                $this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
324
+                $this->access->connection->writeToCache($cacheKey, true);
325
+                return true;
326
+            } catch (ServerNotAvailableException $e) {
327
+                throw $e;
328
+            } catch (\Exception $e) {
329
+                $this->access->connection->writeToCache($cacheKey, false);
330
+                return false;
331
+            }
332
+        }
333
+
334
+        if($user instanceof OfflineUser) {
335
+            $user->unmark();
336
+        }
337
+
338
+        $this->access->connection->writeToCache($cacheKey, true);
339
+        return true;
340
+    }
341
+
342
+    /**
343
+     * check if a user exists
344
+     * @param string $uid the username
345
+     * @return boolean
346
+     * @throws \Exception when connection could not be established
347
+     */
348
+    public function userExists($uid) {
349
+        $userExists = $this->access->connection->getFromCache('userExists'.$uid);
350
+        if(!is_null($userExists)) {
351
+            return (bool)$userExists;
352
+        }
353
+        //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
354
+        $user = $this->access->userManager->get($uid);
355
+
356
+        if(is_null($user)) {
357
+            Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
358
+                $this->access->connection->ldapHost, ILogger::DEBUG);
359
+            $this->access->connection->writeToCache('userExists'.$uid, false);
360
+            return false;
361
+        }
362
+
363
+        $this->access->connection->writeToCache('userExists'.$uid, true);
364
+        return true;
365
+    }
366
+
367
+    /**
368
+     * returns whether a user was deleted in LDAP
369
+     *
370
+     * @param string $uid The username of the user to delete
371
+     * @return bool
372
+     */
373
+    public function deleteUser($uid) {
374
+        if ($this->userPluginManager->canDeleteUser()) {
375
+            $status = $this->userPluginManager->deleteUser($uid);
376
+            if($status === false) {
377
+                return false;
378
+            }
379
+        }
380
+
381
+        $marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
382
+        if((int)$marked === 0) {
383
+            \OC::$server->getLogger()->notice(
384
+                'User '.$uid . ' is not marked as deleted, not cleaning up.',
385
+                ['app' => 'user_ldap']);
386
+            return false;
387
+        }
388
+        \OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
389
+            ['app' => 'user_ldap']);
390
+
391
+        $this->access->getUserMapper()->unmap($uid); // we don't emit unassign signals here, since it is implicit to delete signals fired from core
392
+        $this->access->userManager->invalidate($uid);
393
+        return true;
394
+    }
395
+
396
+    /**
397
+     * get the user's home directory
398
+     *
399
+     * @param string $uid the username
400
+     * @return bool|string
401
+     * @throws NoUserException
402
+     * @throws \Exception
403
+     */
404
+    public function getHome($uid) {
405
+        // user Exists check required as it is not done in user proxy!
406
+        if(!$this->userExists($uid)) {
407
+            return false;
408
+        }
409
+
410
+        if ($this->userPluginManager->implementsActions(Backend::GET_HOME)) {
411
+            return $this->userPluginManager->getHome($uid);
412
+        }
413
+
414
+        $cacheKey = 'getHome'.$uid;
415
+        $path = $this->access->connection->getFromCache($cacheKey);
416
+        if(!is_null($path)) {
417
+            return $path;
418
+        }
419
+
420
+        // early return path if it is a deleted user
421
+        $user = $this->access->userManager->get($uid);
422
+        if($user instanceof User || $user instanceof OfflineUser) {
423
+            $path = $user->getHomePath() ?: false;
424
+        } else {
425
+            throw new NoUserException($uid . ' is not a valid user anymore');
426
+        }
427
+
428
+        $this->access->cacheUserHome($uid, $path);
429
+        return $path;
430
+    }
431
+
432
+    /**
433
+     * get display name of the user
434
+     * @param string $uid user ID of the user
435
+     * @return string|false display name
436
+     */
437
+    public function getDisplayName($uid) {
438
+        if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) {
439
+            return $this->userPluginManager->getDisplayName($uid);
440
+        }
441
+
442
+        if(!$this->userExists($uid)) {
443
+            return false;
444
+        }
445
+
446
+        $cacheKey = 'getDisplayName'.$uid;
447
+        if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
448
+            return $displayName;
449
+        }
450
+
451
+        //Check whether the display name is configured to have a 2nd feature
452
+        $additionalAttribute = $this->access->connection->ldapUserDisplayName2;
453
+        $displayName2 = '';
454
+        if ($additionalAttribute !== '') {
455
+            $displayName2 = $this->access->readAttribute(
456
+                $this->access->username2dn($uid),
457
+                $additionalAttribute);
458
+        }
459
+
460
+        $displayName = $this->access->readAttribute(
461
+            $this->access->username2dn($uid),
462
+            $this->access->connection->ldapUserDisplayName);
463
+
464
+        if($displayName && (count($displayName) > 0)) {
465
+            $displayName = $displayName[0];
466
+
467
+            if (is_array($displayName2)){
468
+                $displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
469
+            }
470
+
471
+            $user = $this->access->userManager->get($uid);
472
+            if ($user instanceof User) {
473
+                $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
474
+                $this->access->connection->writeToCache($cacheKey, $displayName);
475
+            }
476
+            if ($user instanceof OfflineUser) {
477
+                /** @var OfflineUser $user*/
478
+                $displayName = $user->getDisplayName();
479
+            }
480
+            return $displayName;
481
+        }
482
+
483
+        return null;
484
+    }
485
+
486
+    /**
487
+     * set display name of the user
488
+     * @param string $uid user ID of the user
489
+     * @param string $displayName new display name of the user
490
+     * @return string|false display name
491
+     */
492
+    public function setDisplayName($uid, $displayName) {
493
+        if ($this->userPluginManager->implementsActions(Backend::SET_DISPLAYNAME)) {
494
+            $this->userPluginManager->setDisplayName($uid, $displayName);
495
+            $this->access->cacheUserDisplayName($uid, $displayName);
496
+            return $displayName;
497
+        }
498
+        return false;
499
+    }
500
+
501
+    /**
502
+     * Get a list of all display names
503
+     *
504
+     * @param string $search
505
+     * @param string|null $limit
506
+     * @param string|null $offset
507
+     * @return array an array of all displayNames (value) and the corresponding uids (key)
508
+     */
509
+    public function getDisplayNames($search = '', $limit = null, $offset = null) {
510
+        $cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
511
+        if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
512
+            return $displayNames;
513
+        }
514
+
515
+        $displayNames = [];
516
+        $users = $this->getUsers($search, $limit, $offset);
517
+        foreach ($users as $user) {
518
+            $displayNames[$user] = $this->getDisplayName($user);
519
+        }
520
+        $this->access->connection->writeToCache($cacheKey, $displayNames);
521
+        return $displayNames;
522
+    }
523
+
524
+    /**
525
+     * Check if backend implements actions
526
+     * @param int $actions bitwise-or'ed actions
527
+     * @return boolean
528
+     *
529
+     * Returns the supported actions as int to be
530
+     * compared with \OC\User\Backend::CREATE_USER etc.
531
+     */
532
+    public function implementsActions($actions) {
533
+        return (bool)((Backend::CHECK_PASSWORD
534
+            | Backend::GET_HOME
535
+            | Backend::GET_DISPLAYNAME
536
+            | (($this->access->connection->ldapUserAvatarRule !== 'none') ? Backend::PROVIDE_AVATAR : 0)
537
+            | Backend::COUNT_USERS
538
+            | (((int)$this->access->connection->turnOnPasswordChange === 1)? Backend::SET_PASSWORD :0)
539
+            | $this->userPluginManager->getImplementedActions())
540
+            & $actions);
541
+    }
542
+
543
+    /**
544
+     * @return bool
545
+     */
546
+    public function hasUserListings() {
547
+        return true;
548
+    }
549
+
550
+    /**
551
+     * counts the users in LDAP
552
+     *
553
+     * @return int|bool
554
+     */
555
+    public function countUsers() {
556
+        if ($this->userPluginManager->implementsActions(Backend::COUNT_USERS)) {
557
+            return $this->userPluginManager->countUsers();
558
+        }
559
+
560
+        $filter = $this->access->getFilterForUserCount();
561
+        $cacheKey = 'countUsers-'.$filter;
562
+        if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
563
+            return $entries;
564
+        }
565
+        $entries = $this->access->countUsers($filter);
566
+        $this->access->connection->writeToCache($cacheKey, $entries);
567
+        return $entries;
568
+    }
569
+
570
+    /**
571
+     * Backend name to be shown in user management
572
+     * @return string the name of the backend to be shown
573
+     */
574
+    public function getBackendName(){
575
+        return 'LDAP';
576
+    }
577 577
 	
578
-	/**
579
-	 * Return access for LDAP interaction.
580
-	 * @param string $uid
581
-	 * @return Access instance of Access for LDAP interaction
582
-	 */
583
-	public function getLDAPAccess($uid) {
584
-		return $this->access;
585
-	}
578
+    /**
579
+     * Return access for LDAP interaction.
580
+     * @param string $uid
581
+     * @return Access instance of Access for LDAP interaction
582
+     */
583
+    public function getLDAPAccess($uid) {
584
+        return $this->access;
585
+    }
586 586
 	
587
-	/**
588
-	 * Return LDAP connection resource from a cloned connection.
589
-	 * The cloned connection needs to be closed manually.
590
-	 * of the current access.
591
-	 * @param string $uid
592
-	 * @return resource of the LDAP connection
593
-	 */
594
-	public function getNewLDAPConnection($uid) {
595
-		$connection = clone $this->access->getConnection();
596
-		return $connection->getConnectionResource();
597
-	}
598
-
599
-	/**
600
-	 * create new user
601
-	 * @param string $username username of the new user
602
-	 * @param string $password password of the new user
603
-	 * @throws \UnexpectedValueException
604
-	 * @return bool
605
-	 */
606
-	public function createUser($username, $password) {
607
-		if ($this->userPluginManager->implementsActions(Backend::CREATE_USER)) {
608
-			if ($dn = $this->userPluginManager->createUser($username, $password)) {
609
-				if (is_string($dn)) {
610
-					// the NC user creation work flow requires a know user id up front
611
-					$uuid = $this->access->getUUID($dn, true);
612
-					if(is_string($uuid)) {
613
-						$this->access->mapAndAnnounceIfApplicable(
614
-							$this->access->getUserMapper(),
615
-							$dn,
616
-							$username,
617
-							$uuid,
618
-							true
619
-						);
620
-						$this->access->cacheUserExists($username);
621
-					} else {
622
-						\OC::$server->getLogger()->warning(
623
-							'Failed to map created LDAP user with userid {userid}, because UUID could not be determined',
624
-							[
625
-								'app' => 'user_ldap',
626
-								'userid' => $username,
627
-							]
628
-						);
629
-					}
630
-				} else {
631
-					throw new \UnexpectedValueException("LDAP Plugin: Method createUser changed to return the user DN instead of boolean.");
632
-				}
633
-			}
634
-			return (bool) $dn;
635
-		}
636
-		return false;
637
-	}
587
+    /**
588
+     * Return LDAP connection resource from a cloned connection.
589
+     * The cloned connection needs to be closed manually.
590
+     * of the current access.
591
+     * @param string $uid
592
+     * @return resource of the LDAP connection
593
+     */
594
+    public function getNewLDAPConnection($uid) {
595
+        $connection = clone $this->access->getConnection();
596
+        return $connection->getConnectionResource();
597
+    }
598
+
599
+    /**
600
+     * create new user
601
+     * @param string $username username of the new user
602
+     * @param string $password password of the new user
603
+     * @throws \UnexpectedValueException
604
+     * @return bool
605
+     */
606
+    public function createUser($username, $password) {
607
+        if ($this->userPluginManager->implementsActions(Backend::CREATE_USER)) {
608
+            if ($dn = $this->userPluginManager->createUser($username, $password)) {
609
+                if (is_string($dn)) {
610
+                    // the NC user creation work flow requires a know user id up front
611
+                    $uuid = $this->access->getUUID($dn, true);
612
+                    if(is_string($uuid)) {
613
+                        $this->access->mapAndAnnounceIfApplicable(
614
+                            $this->access->getUserMapper(),
615
+                            $dn,
616
+                            $username,
617
+                            $uuid,
618
+                            true
619
+                        );
620
+                        $this->access->cacheUserExists($username);
621
+                    } else {
622
+                        \OC::$server->getLogger()->warning(
623
+                            'Failed to map created LDAP user with userid {userid}, because UUID could not be determined',
624
+                            [
625
+                                'app' => 'user_ldap',
626
+                                'userid' => $username,
627
+                            ]
628
+                        );
629
+                    }
630
+                } else {
631
+                    throw new \UnexpectedValueException("LDAP Plugin: Method createUser changed to return the user DN instead of boolean.");
632
+                }
633
+            }
634
+            return (bool) $dn;
635
+        }
636
+        return false;
637
+    }
638 638
 
639 639
 }
Please login to merge, or discard this patch.
Spacing   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -87,16 +87,16 @@  discard block
 block discarded – undo
87 87
 			return $this->userPluginManager->canChangeAvatar($uid);
88 88
 		}
89 89
 
90
-		if(!$this->implementsActions(Backend::PROVIDE_AVATAR)) {
90
+		if (!$this->implementsActions(Backend::PROVIDE_AVATAR)) {
91 91
 			return true;
92 92
 		}
93 93
 
94 94
 		$user = $this->access->userManager->get($uid);
95
-		if(!$user instanceof User) {
95
+		if (!$user instanceof User) {
96 96
 			return false;
97 97
 		}
98 98
 		$imageData = $user->getAvatarImage();
99
-		if($imageData === false) {
99
+		if ($imageData === false) {
100 100
 			return true;
101 101
 		}
102 102
 		return !$user->updateAvatar(true);
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 * @throws \Exception
111 111
 	 */
112 112
 	public function loginName2UserName($loginName) {
113
-		$cacheKey = 'loginName2UserName-' . $loginName;
113
+		$cacheKey = 'loginName2UserName-'.$loginName;
114 114
 		$username = $this->access->connection->getFromCache($cacheKey);
115 115
 
116 116
 		if ($username !== null) {
@@ -156,9 +156,9 @@  discard block
 block discarded – undo
156 156
 		//find out dn of the user name
157 157
 		$attrs = $this->access->userManager->getAttributes();
158 158
 		$users = $this->access->fetchUsersByLoginName($loginName, $attrs);
159
-		if(count($users) < 1) {
160
-			throw new NotOnLDAP('No user available for the given login name on ' .
161
-				$this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
159
+		if (count($users) < 1) {
160
+			throw new NotOnLDAP('No user available for the given login name on '.
161
+				$this->access->connection->ldapHost.':'.$this->access->connection->ldapPort);
162 162
 		}
163 163
 		return $users[0];
164 164
 	}
@@ -173,23 +173,23 @@  discard block
 block discarded – undo
173 173
 	public function checkPassword($uid, $password) {
174 174
 		try {
175 175
 			$ldapRecord = $this->getLDAPUserByLoginName($uid);
176
-		} catch(NotOnLDAP $e) {
176
+		} catch (NotOnLDAP $e) {
177 177
 			\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap', 'level' => ILogger::DEBUG]);
178 178
 			return false;
179 179
 		}
180 180
 		$dn = $ldapRecord['dn'][0];
181 181
 		$user = $this->access->userManager->get($dn);
182 182
 
183
-		if(!$user instanceof User) {
183
+		if (!$user instanceof User) {
184 184
 			Util::writeLog('user_ldap',
185
-				'LDAP Login: Could not get user object for DN ' . $dn .
185
+				'LDAP Login: Could not get user object for DN '.$dn.
186 186
 				'. Maybe the LDAP entry has no set display name attribute?',
187 187
 				ILogger::WARN);
188 188
 			return false;
189 189
 		}
190
-		if($user->getUsername() !== false) {
190
+		if ($user->getUsername() !== false) {
191 191
 			//are the credentials OK?
192
-			if(!$this->access->areCredentialsValid($dn, $password)) {
192
+			if (!$this->access->areCredentialsValid($dn, $password)) {
193 193
 				return false;
194 194
 			}
195 195
 
@@ -216,14 +216,14 @@  discard block
 block discarded – undo
216 216
 
217 217
 		$user = $this->access->userManager->get($uid);
218 218
 
219
-		if(!$user instanceof User) {
220
-			throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
219
+		if (!$user instanceof User) {
220
+			throw new \Exception('LDAP setPassword: Could not get user object for uid '.$uid.
221 221
 				'. Maybe the LDAP entry has no set display name attribute?');
222 222
 		}
223
-		if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
223
+		if ($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
224 224
 			$ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
225 225
 			$turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
226
-			if (!empty($ldapDefaultPPolicyDN) && ((int)$turnOnPasswordChange === 1)) {
226
+			if (!empty($ldapDefaultPPolicyDN) && ((int) $turnOnPasswordChange === 1)) {
227 227
 				//remove last password expiry warning if any
228 228
 				$notification = $this->notificationManager->createNotification();
229 229
 				$notification->setApp('user_ldap')
@@ -252,18 +252,18 @@  discard block
 block discarded – undo
252 252
 
253 253
 		//check if users are cached, if so return
254 254
 		$ldap_users = $this->access->connection->getFromCache($cachekey);
255
-		if(!is_null($ldap_users)) {
255
+		if (!is_null($ldap_users)) {
256 256
 			return $ldap_users;
257 257
 		}
258 258
 
259 259
 		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
260 260
 		// error. With a limit of 0, we get 0 results. So we pass null.
261
-		if($limit <= 0) {
261
+		if ($limit <= 0) {
262 262
 			$limit = null;
263 263
 		}
264 264
 		$filter = $this->access->combineFilterWithAnd([
265 265
 			$this->access->connection->ldapUserFilter,
266
-			$this->access->connection->ldapUserDisplayName . '=*',
266
+			$this->access->connection->ldapUserDisplayName.'=*',
267 267
 			$this->access->getFilterPartForUserSearch($search)
268 268
 		]);
269 269
 
@@ -276,7 +276,7 @@  discard block
 block discarded – undo
276 276
 			$this->access->userManager->getAttributes(true),
277 277
 			$limit, $offset);
278 278
 		$ldap_users = $this->access->nextcloudUserNames($ldap_users);
279
-		Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', ILogger::DEBUG);
279
+		Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users).' Users found', ILogger::DEBUG);
280 280
 
281 281
 		$this->access->connection->writeToCache($cachekey, $ldap_users);
282 282
 		return $ldap_users;
@@ -292,22 +292,22 @@  discard block
 block discarded – undo
292 292
 	 * @throws \OC\ServerNotAvailableException
293 293
 	 */
294 294
 	public function userExistsOnLDAP($user) {
295
-		if(is_string($user)) {
295
+		if (is_string($user)) {
296 296
 			$user = $this->access->userManager->get($user);
297 297
 		}
298
-		if(is_null($user)) {
298
+		if (is_null($user)) {
299 299
 			return false;
300 300
 		}
301 301
 		$uid = $user instanceof User ? $user->getUsername() : $user->getOCName();
302
-		$cacheKey = 'userExistsOnLDAP' . $uid;
302
+		$cacheKey = 'userExistsOnLDAP'.$uid;
303 303
 		$userExists = $this->access->connection->getFromCache($cacheKey);
304
-		if(!is_null($userExists)) {
305
-			return (bool)$userExists;
304
+		if (!is_null($userExists)) {
305
+			return (bool) $userExists;
306 306
 		}
307 307
 
308 308
 		$dn = $user->getDN();
309 309
 		//check if user really still exists by reading its entry
310
-		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
310
+		if (!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
311 311
 			try {
312 312
 				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
313 313
 				if (!$uuid) {
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 			}
332 332
 		}
333 333
 
334
-		if($user instanceof OfflineUser) {
334
+		if ($user instanceof OfflineUser) {
335 335
 			$user->unmark();
336 336
 		}
337 337
 
@@ -347,13 +347,13 @@  discard block
 block discarded – undo
347 347
 	 */
348 348
 	public function userExists($uid) {
349 349
 		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
350
-		if(!is_null($userExists)) {
351
-			return (bool)$userExists;
350
+		if (!is_null($userExists)) {
351
+			return (bool) $userExists;
352 352
 		}
353 353
 		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
354 354
 		$user = $this->access->userManager->get($uid);
355 355
 
356
-		if(is_null($user)) {
356
+		if (is_null($user)) {
357 357
 			Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
358 358
 				$this->access->connection->ldapHost, ILogger::DEBUG);
359 359
 			$this->access->connection->writeToCache('userExists'.$uid, false);
@@ -373,19 +373,19 @@  discard block
 block discarded – undo
373 373
 	public function deleteUser($uid) {
374 374
 		if ($this->userPluginManager->canDeleteUser()) {
375 375
 			$status = $this->userPluginManager->deleteUser($uid);
376
-			if($status === false) {
376
+			if ($status === false) {
377 377
 				return false;
378 378
 			}
379 379
 		}
380 380
 
381 381
 		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
382
-		if((int)$marked === 0) {
382
+		if ((int) $marked === 0) {
383 383
 			\OC::$server->getLogger()->notice(
384
-				'User '.$uid . ' is not marked as deleted, not cleaning up.',
384
+				'User '.$uid.' is not marked as deleted, not cleaning up.',
385 385
 				['app' => 'user_ldap']);
386 386
 			return false;
387 387
 		}
388
-		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
388
+		\OC::$server->getLogger()->info('Cleaning up after user '.$uid,
389 389
 			['app' => 'user_ldap']);
390 390
 
391 391
 		$this->access->getUserMapper()->unmap($uid); // we don't emit unassign signals here, since it is implicit to delete signals fired from core
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 	 */
404 404
 	public function getHome($uid) {
405 405
 		// user Exists check required as it is not done in user proxy!
406
-		if(!$this->userExists($uid)) {
406
+		if (!$this->userExists($uid)) {
407 407
 			return false;
408 408
 		}
409 409
 
@@ -413,16 +413,16 @@  discard block
 block discarded – undo
413 413
 
414 414
 		$cacheKey = 'getHome'.$uid;
415 415
 		$path = $this->access->connection->getFromCache($cacheKey);
416
-		if(!is_null($path)) {
416
+		if (!is_null($path)) {
417 417
 			return $path;
418 418
 		}
419 419
 
420 420
 		// early return path if it is a deleted user
421 421
 		$user = $this->access->userManager->get($uid);
422
-		if($user instanceof User || $user instanceof OfflineUser) {
422
+		if ($user instanceof User || $user instanceof OfflineUser) {
423 423
 			$path = $user->getHomePath() ?: false;
424 424
 		} else {
425
-			throw new NoUserException($uid . ' is not a valid user anymore');
425
+			throw new NoUserException($uid.' is not a valid user anymore');
426 426
 		}
427 427
 
428 428
 		$this->access->cacheUserHome($uid, $path);
@@ -439,12 +439,12 @@  discard block
 block discarded – undo
439 439
 			return $this->userPluginManager->getDisplayName($uid);
440 440
 		}
441 441
 
442
-		if(!$this->userExists($uid)) {
442
+		if (!$this->userExists($uid)) {
443 443
 			return false;
444 444
 		}
445 445
 
446 446
 		$cacheKey = 'getDisplayName'.$uid;
447
-		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
447
+		if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
448 448
 			return $displayName;
449 449
 		}
450 450
 
@@ -461,10 +461,10 @@  discard block
 block discarded – undo
461 461
 			$this->access->username2dn($uid),
462 462
 			$this->access->connection->ldapUserDisplayName);
463 463
 
464
-		if($displayName && (count($displayName) > 0)) {
464
+		if ($displayName && (count($displayName) > 0)) {
465 465
 			$displayName = $displayName[0];
466 466
 
467
-			if (is_array($displayName2)){
467
+			if (is_array($displayName2)) {
468 468
 				$displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
469 469
 			}
470 470
 
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
 	 */
509 509
 	public function getDisplayNames($search = '', $limit = null, $offset = null) {
510 510
 		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
511
-		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
511
+		if (!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
512 512
 			return $displayNames;
513 513
 		}
514 514
 
@@ -530,12 +530,12 @@  discard block
 block discarded – undo
530 530
 	 * compared with \OC\User\Backend::CREATE_USER etc.
531 531
 	 */
532 532
 	public function implementsActions($actions) {
533
-		return (bool)((Backend::CHECK_PASSWORD
533
+		return (bool) ((Backend::CHECK_PASSWORD
534 534
 			| Backend::GET_HOME
535 535
 			| Backend::GET_DISPLAYNAME
536 536
 			| (($this->access->connection->ldapUserAvatarRule !== 'none') ? Backend::PROVIDE_AVATAR : 0)
537 537
 			| Backend::COUNT_USERS
538
-			| (((int)$this->access->connection->turnOnPasswordChange === 1)? Backend::SET_PASSWORD :0)
538
+			| (((int) $this->access->connection->turnOnPasswordChange === 1) ? Backend::SET_PASSWORD : 0)
539 539
 			| $this->userPluginManager->getImplementedActions())
540 540
 			& $actions);
541 541
 	}
@@ -559,7 +559,7 @@  discard block
 block discarded – undo
559 559
 
560 560
 		$filter = $this->access->getFilterForUserCount();
561 561
 		$cacheKey = 'countUsers-'.$filter;
562
-		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
562
+		if (!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
563 563
 			return $entries;
564 564
 		}
565 565
 		$entries = $this->access->countUsers($filter);
@@ -571,7 +571,7 @@  discard block
 block discarded – undo
571 571
 	 * Backend name to be shown in user management
572 572
 	 * @return string the name of the backend to be shown
573 573
 	 */
574
-	public function getBackendName(){
574
+	public function getBackendName() {
575 575
 		return 'LDAP';
576 576
 	}
577 577
 	
@@ -609,7 +609,7 @@  discard block
 block discarded – undo
609 609
 				if (is_string($dn)) {
610 610
 					// the NC user creation work flow requires a know user id up front
611 611
 					$uuid = $this->access->getUUID($dn, true);
612
-					if(is_string($uuid)) {
612
+					if (is_string($uuid)) {
613 613
 						$this->access->mapAndAnnounceIfApplicable(
614 614
 							$this->access->getUserMapper(),
615 615
 							$dn,
Please login to merge, or discard this patch.
apps/settings/lib/Controller/CheckSetupController.php 2 patches
Indentation   +637 added lines, -637 removed lines patch added patch discarded remove patch
@@ -70,288 +70,288 @@  discard block
 block discarded – undo
70 70
 use Symfony\Component\EventDispatcher\GenericEvent;
71 71
 
72 72
 class CheckSetupController extends Controller {
73
-	/** @var IConfig */
74
-	private $config;
75
-	/** @var IClientService */
76
-	private $clientService;
77
-	/** @var IURLGenerator */
78
-	private $urlGenerator;
79
-	/** @var IL10N */
80
-	private $l10n;
81
-	/** @var Checker */
82
-	private $checker;
83
-	/** @var ILogger */
84
-	private $logger;
85
-	/** @var EventDispatcherInterface */
86
-	private $dispatcher;
87
-	/** @var IDBConnection|Connection */
88
-	private $db;
89
-	/** @var ILockingProvider */
90
-	private $lockingProvider;
91
-	/** @var IDateTimeFormatter */
92
-	private $dateTimeFormatter;
93
-	/** @var MemoryInfo */
94
-	private $memoryInfo;
95
-	/** @var ISecureRandom */
96
-	private $secureRandom;
97
-
98
-	public function __construct($AppName,
99
-								IRequest $request,
100
-								IConfig $config,
101
-								IClientService $clientService,
102
-								IURLGenerator $urlGenerator,
103
-								IL10N $l10n,
104
-								Checker $checker,
105
-								ILogger $logger,
106
-								EventDispatcherInterface $dispatcher,
107
-								IDBConnection $db,
108
-								ILockingProvider $lockingProvider,
109
-								IDateTimeFormatter $dateTimeFormatter,
110
-								MemoryInfo $memoryInfo,
111
-								ISecureRandom $secureRandom) {
112
-		parent::__construct($AppName, $request);
113
-		$this->config = $config;
114
-		$this->clientService = $clientService;
115
-		$this->urlGenerator = $urlGenerator;
116
-		$this->l10n = $l10n;
117
-		$this->checker = $checker;
118
-		$this->logger = $logger;
119
-		$this->dispatcher = $dispatcher;
120
-		$this->db = $db;
121
-		$this->lockingProvider = $lockingProvider;
122
-		$this->dateTimeFormatter = $dateTimeFormatter;
123
-		$this->memoryInfo = $memoryInfo;
124
-		$this->secureRandom = $secureRandom;
125
-	}
126
-
127
-	/**
128
-	 * Checks if the server can connect to the internet using HTTPS and HTTP
129
-	 * @return bool
130
-	 */
131
-	private function hasInternetConnectivityProblems(): bool {
132
-		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
133
-			return false;
134
-		}
135
-
136
-		$siteArray = $this->config->getSystemValue('connectivity_check_domains', [
137
-			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
138
-		]);
139
-
140
-		foreach($siteArray as $site) {
141
-			if ($this->isSiteReachable($site)) {
142
-				return false;
143
-			}
144
-		}
145
-		return true;
146
-	}
147
-
148
-	/**
149
-	 * Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
150
-	 * @return bool
151
-	 */
152
-	private function isSiteReachable($sitename) {
153
-		$httpSiteName = 'http://' . $sitename . '/';
154
-		$httpsSiteName = 'https://' . $sitename . '/';
155
-
156
-		try {
157
-			$client = $this->clientService->newClient();
158
-			$client->get($httpSiteName);
159
-			$client->get($httpsSiteName);
160
-		} catch (\Exception $e) {
161
-			$this->logger->logException($e, ['app' => 'internet_connection_check']);
162
-			return false;
163
-		}
164
-		return true;
165
-	}
166
-
167
-	/**
168
-	 * Checks whether a local memcache is installed or not
169
-	 * @return bool
170
-	 */
171
-	private function isMemcacheConfigured() {
172
-		return $this->config->getSystemValue('memcache.local', null) !== null;
173
-	}
174
-
175
-	/**
176
-	 * Whether PHP can generate "secure" pseudorandom integers
177
-	 *
178
-	 * @return bool
179
-	 */
180
-	private function isRandomnessSecure() {
181
-		try {
182
-			$this->secureRandom->generate(1);
183
-		} catch (\Exception $ex) {
184
-			return false;
185
-		}
186
-		return true;
187
-	}
188
-
189
-	/**
190
-	 * Public for the sake of unit-testing
191
-	 *
192
-	 * @return array
193
-	 */
194
-	protected function getCurlVersion() {
195
-		return curl_version();
196
-	}
197
-
198
-	/**
199
-	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
200
-	 * have multiple bugs which likely lead to problems in combination with
201
-	 * functionality required by ownCloud such as SNI.
202
-	 *
203
-	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
204
-	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
205
-	 * @return string
206
-	 */
207
-	private function isUsedTlsLibOutdated() {
208
-		// Don't run check when:
209
-		// 1. Server has `has_internet_connection` set to false
210
-		// 2. AppStore AND S2S is disabled
211
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
212
-			return '';
213
-		}
214
-		if(!$this->config->getSystemValue('appstoreenabled', true)
215
-			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
216
-			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
217
-			return '';
218
-		}
219
-
220
-		$versionString = $this->getCurlVersion();
221
-		if(isset($versionString['ssl_version'])) {
222
-			$versionString = $versionString['ssl_version'];
223
-		} else {
224
-			return '';
225
-		}
226
-
227
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
228
-		if(!$this->config->getSystemValue('appstoreenabled', true)) {
229
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
230
-		}
231
-
232
-		// Check if at least OpenSSL after 1.01d or 1.0.2b
233
-		if(strpos($versionString, 'OpenSSL/') === 0) {
234
-			$majorVersion = substr($versionString, 8, 5);
235
-			$patchRelease = substr($versionString, 13, 6);
236
-
237
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
238
-				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
239
-				return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
240
-			}
241
-		}
242
-
243
-		// Check if NSS and perform heuristic check
244
-		if(strpos($versionString, 'NSS/') === 0) {
245
-			try {
246
-				$firstClient = $this->clientService->newClient();
247
-				$firstClient->get('https://nextcloud.com/');
248
-
249
-				$secondClient = $this->clientService->newClient();
250
-				$secondClient->get('https://nextcloud.com/');
251
-			} catch (ClientException $e) {
252
-				if($e->getResponse()->getStatusCode() === 400) {
253
-					return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
254
-				}
255
-			}
256
-		}
257
-
258
-		return '';
259
-	}
260
-
261
-	/**
262
-	 * Whether the version is outdated
263
-	 *
264
-	 * @return bool
265
-	 */
266
-	protected function isPhpOutdated(): bool {
267
-		return PHP_VERSION_ID < 70300;
268
-	}
269
-
270
-	/**
271
-	 * Whether the php version is still supported (at time of release)
272
-	 * according to: https://secure.php.net/supported-versions.php
273
-	 *
274
-	 * @return array
275
-	 */
276
-	private function isPhpSupported(): array {
277
-		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
278
-	}
279
-
280
-	/**
281
-	 * Check if the reverse proxy configuration is working as expected
282
-	 *
283
-	 * @return bool
284
-	 */
285
-	private function forwardedForHeadersWorking() {
286
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
287
-		$remoteAddress = $this->request->getHeader('REMOTE_ADDR');
288
-
289
-		if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
290
-			return false;
291
-		}
292
-
293
-		if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
294
-			return $remoteAddress !== $this->request->getRemoteAddress();
295
-		}
296
-
297
-		// either not enabled or working correctly
298
-		return true;
299
-	}
300
-
301
-	/**
302
-	 * Checks if the correct memcache module for PHP is installed. Only
303
-	 * fails if memcached is configured and the working module is not installed.
304
-	 *
305
-	 * @return bool
306
-	 */
307
-	private function isCorrectMemcachedPHPModuleInstalled() {
308
-		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
309
-			return true;
310
-		}
311
-
312
-		// there are two different memcached modules for PHP
313
-		// we only support memcached and not memcache
314
-		// https://code.google.com/p/memcached/wiki/PHPClientComparison
315
-		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
316
-	}
317
-
318
-	/**
319
-	 * Checks if set_time_limit is not disabled.
320
-	 *
321
-	 * @return bool
322
-	 */
323
-	private function isSettimelimitAvailable() {
324
-		if (function_exists('set_time_limit')
325
-			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
326
-			return true;
327
-		}
328
-
329
-		return false;
330
-	}
331
-
332
-	/**
333
-	 * @return RedirectResponse
334
-	 */
335
-	public function rescanFailedIntegrityCheck() {
336
-		$this->checker->runInstanceVerification();
337
-		return new RedirectResponse(
338
-			$this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview'])
339
-		);
340
-	}
341
-
342
-	/**
343
-	 * @NoCSRFRequired
344
-	 * @return DataResponse
345
-	 */
346
-	public function getFailedIntegrityCheckFiles() {
347
-		if(!$this->checker->isCodeCheckEnforced()) {
348
-			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
349
-		}
350
-
351
-		$completeResults = $this->checker->getResults();
352
-
353
-		if(!empty($completeResults)) {
354
-			$formattedTextResponse = 'Technical information
73
+    /** @var IConfig */
74
+    private $config;
75
+    /** @var IClientService */
76
+    private $clientService;
77
+    /** @var IURLGenerator */
78
+    private $urlGenerator;
79
+    /** @var IL10N */
80
+    private $l10n;
81
+    /** @var Checker */
82
+    private $checker;
83
+    /** @var ILogger */
84
+    private $logger;
85
+    /** @var EventDispatcherInterface */
86
+    private $dispatcher;
87
+    /** @var IDBConnection|Connection */
88
+    private $db;
89
+    /** @var ILockingProvider */
90
+    private $lockingProvider;
91
+    /** @var IDateTimeFormatter */
92
+    private $dateTimeFormatter;
93
+    /** @var MemoryInfo */
94
+    private $memoryInfo;
95
+    /** @var ISecureRandom */
96
+    private $secureRandom;
97
+
98
+    public function __construct($AppName,
99
+                                IRequest $request,
100
+                                IConfig $config,
101
+                                IClientService $clientService,
102
+                                IURLGenerator $urlGenerator,
103
+                                IL10N $l10n,
104
+                                Checker $checker,
105
+                                ILogger $logger,
106
+                                EventDispatcherInterface $dispatcher,
107
+                                IDBConnection $db,
108
+                                ILockingProvider $lockingProvider,
109
+                                IDateTimeFormatter $dateTimeFormatter,
110
+                                MemoryInfo $memoryInfo,
111
+                                ISecureRandom $secureRandom) {
112
+        parent::__construct($AppName, $request);
113
+        $this->config = $config;
114
+        $this->clientService = $clientService;
115
+        $this->urlGenerator = $urlGenerator;
116
+        $this->l10n = $l10n;
117
+        $this->checker = $checker;
118
+        $this->logger = $logger;
119
+        $this->dispatcher = $dispatcher;
120
+        $this->db = $db;
121
+        $this->lockingProvider = $lockingProvider;
122
+        $this->dateTimeFormatter = $dateTimeFormatter;
123
+        $this->memoryInfo = $memoryInfo;
124
+        $this->secureRandom = $secureRandom;
125
+    }
126
+
127
+    /**
128
+     * Checks if the server can connect to the internet using HTTPS and HTTP
129
+     * @return bool
130
+     */
131
+    private function hasInternetConnectivityProblems(): bool {
132
+        if ($this->config->getSystemValue('has_internet_connection', true) === false) {
133
+            return false;
134
+        }
135
+
136
+        $siteArray = $this->config->getSystemValue('connectivity_check_domains', [
137
+            'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
138
+        ]);
139
+
140
+        foreach($siteArray as $site) {
141
+            if ($this->isSiteReachable($site)) {
142
+                return false;
143
+            }
144
+        }
145
+        return true;
146
+    }
147
+
148
+    /**
149
+     * Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
150
+     * @return bool
151
+     */
152
+    private function isSiteReachable($sitename) {
153
+        $httpSiteName = 'http://' . $sitename . '/';
154
+        $httpsSiteName = 'https://' . $sitename . '/';
155
+
156
+        try {
157
+            $client = $this->clientService->newClient();
158
+            $client->get($httpSiteName);
159
+            $client->get($httpsSiteName);
160
+        } catch (\Exception $e) {
161
+            $this->logger->logException($e, ['app' => 'internet_connection_check']);
162
+            return false;
163
+        }
164
+        return true;
165
+    }
166
+
167
+    /**
168
+     * Checks whether a local memcache is installed or not
169
+     * @return bool
170
+     */
171
+    private function isMemcacheConfigured() {
172
+        return $this->config->getSystemValue('memcache.local', null) !== null;
173
+    }
174
+
175
+    /**
176
+     * Whether PHP can generate "secure" pseudorandom integers
177
+     *
178
+     * @return bool
179
+     */
180
+    private function isRandomnessSecure() {
181
+        try {
182
+            $this->secureRandom->generate(1);
183
+        } catch (\Exception $ex) {
184
+            return false;
185
+        }
186
+        return true;
187
+    }
188
+
189
+    /**
190
+     * Public for the sake of unit-testing
191
+     *
192
+     * @return array
193
+     */
194
+    protected function getCurlVersion() {
195
+        return curl_version();
196
+    }
197
+
198
+    /**
199
+     * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
200
+     * have multiple bugs which likely lead to problems in combination with
201
+     * functionality required by ownCloud such as SNI.
202
+     *
203
+     * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
204
+     * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
205
+     * @return string
206
+     */
207
+    private function isUsedTlsLibOutdated() {
208
+        // Don't run check when:
209
+        // 1. Server has `has_internet_connection` set to false
210
+        // 2. AppStore AND S2S is disabled
211
+        if(!$this->config->getSystemValue('has_internet_connection', true)) {
212
+            return '';
213
+        }
214
+        if(!$this->config->getSystemValue('appstoreenabled', true)
215
+            && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
216
+            && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
217
+            return '';
218
+        }
219
+
220
+        $versionString = $this->getCurlVersion();
221
+        if(isset($versionString['ssl_version'])) {
222
+            $versionString = $versionString['ssl_version'];
223
+        } else {
224
+            return '';
225
+        }
226
+
227
+        $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
228
+        if(!$this->config->getSystemValue('appstoreenabled', true)) {
229
+            $features = (string)$this->l10n->t('Federated Cloud Sharing');
230
+        }
231
+
232
+        // Check if at least OpenSSL after 1.01d or 1.0.2b
233
+        if(strpos($versionString, 'OpenSSL/') === 0) {
234
+            $majorVersion = substr($versionString, 8, 5);
235
+            $patchRelease = substr($versionString, 13, 6);
236
+
237
+            if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
238
+                ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
239
+                return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
240
+            }
241
+        }
242
+
243
+        // Check if NSS and perform heuristic check
244
+        if(strpos($versionString, 'NSS/') === 0) {
245
+            try {
246
+                $firstClient = $this->clientService->newClient();
247
+                $firstClient->get('https://nextcloud.com/');
248
+
249
+                $secondClient = $this->clientService->newClient();
250
+                $secondClient->get('https://nextcloud.com/');
251
+            } catch (ClientException $e) {
252
+                if($e->getResponse()->getStatusCode() === 400) {
253
+                    return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
254
+                }
255
+            }
256
+        }
257
+
258
+        return '';
259
+    }
260
+
261
+    /**
262
+     * Whether the version is outdated
263
+     *
264
+     * @return bool
265
+     */
266
+    protected function isPhpOutdated(): bool {
267
+        return PHP_VERSION_ID < 70300;
268
+    }
269
+
270
+    /**
271
+     * Whether the php version is still supported (at time of release)
272
+     * according to: https://secure.php.net/supported-versions.php
273
+     *
274
+     * @return array
275
+     */
276
+    private function isPhpSupported(): array {
277
+        return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
278
+    }
279
+
280
+    /**
281
+     * Check if the reverse proxy configuration is working as expected
282
+     *
283
+     * @return bool
284
+     */
285
+    private function forwardedForHeadersWorking() {
286
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
287
+        $remoteAddress = $this->request->getHeader('REMOTE_ADDR');
288
+
289
+        if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
290
+            return false;
291
+        }
292
+
293
+        if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
294
+            return $remoteAddress !== $this->request->getRemoteAddress();
295
+        }
296
+
297
+        // either not enabled or working correctly
298
+        return true;
299
+    }
300
+
301
+    /**
302
+     * Checks if the correct memcache module for PHP is installed. Only
303
+     * fails if memcached is configured and the working module is not installed.
304
+     *
305
+     * @return bool
306
+     */
307
+    private function isCorrectMemcachedPHPModuleInstalled() {
308
+        if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
309
+            return true;
310
+        }
311
+
312
+        // there are two different memcached modules for PHP
313
+        // we only support memcached and not memcache
314
+        // https://code.google.com/p/memcached/wiki/PHPClientComparison
315
+        return !(!extension_loaded('memcached') && extension_loaded('memcache'));
316
+    }
317
+
318
+    /**
319
+     * Checks if set_time_limit is not disabled.
320
+     *
321
+     * @return bool
322
+     */
323
+    private function isSettimelimitAvailable() {
324
+        if (function_exists('set_time_limit')
325
+            && strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
326
+            return true;
327
+        }
328
+
329
+        return false;
330
+    }
331
+
332
+    /**
333
+     * @return RedirectResponse
334
+     */
335
+    public function rescanFailedIntegrityCheck() {
336
+        $this->checker->runInstanceVerification();
337
+        return new RedirectResponse(
338
+            $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview'])
339
+        );
340
+    }
341
+
342
+    /**
343
+     * @NoCSRFRequired
344
+     * @return DataResponse
345
+     */
346
+    public function getFailedIntegrityCheckFiles() {
347
+        if(!$this->checker->isCodeCheckEnforced()) {
348
+            return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
349
+        }
350
+
351
+        $completeResults = $this->checker->getResults();
352
+
353
+        if(!empty($completeResults)) {
354
+            $formattedTextResponse = 'Technical information
355 355
 =====================
356 356
 The following list covers which files have failed the integrity check. Please read
357 357
 the previous linked documentation to learn more about the errors and how to fix
@@ -360,362 +360,362 @@  discard block
 block discarded – undo
360 360
 Results
361 361
 =======
362 362
 ';
363
-			foreach($completeResults as $context => $contextResult) {
364
-				$formattedTextResponse .= "- $context\n";
365
-
366
-				foreach($contextResult as $category => $result) {
367
-					$formattedTextResponse .= "\t- $category\n";
368
-					if($category !== 'EXCEPTION') {
369
-						foreach ($result as $key => $results) {
370
-							$formattedTextResponse .= "\t\t- $key\n";
371
-						}
372
-					} else {
373
-						foreach ($result as $key => $results) {
374
-							$formattedTextResponse .= "\t\t- $results\n";
375
-						}
376
-					}
377
-
378
-				}
379
-			}
380
-
381
-			$formattedTextResponse .= '
363
+            foreach($completeResults as $context => $contextResult) {
364
+                $formattedTextResponse .= "- $context\n";
365
+
366
+                foreach($contextResult as $category => $result) {
367
+                    $formattedTextResponse .= "\t- $category\n";
368
+                    if($category !== 'EXCEPTION') {
369
+                        foreach ($result as $key => $results) {
370
+                            $formattedTextResponse .= "\t\t- $key\n";
371
+                        }
372
+                    } else {
373
+                        foreach ($result as $key => $results) {
374
+                            $formattedTextResponse .= "\t\t- $results\n";
375
+                        }
376
+                    }
377
+
378
+                }
379
+            }
380
+
381
+            $formattedTextResponse .= '
382 382
 Raw output
383 383
 ==========
384 384
 ';
385
-			$formattedTextResponse .= print_r($completeResults, true);
386
-		} else {
387
-			$formattedTextResponse = 'No errors have been found.';
388
-		}
389
-
390
-
391
-		$response = new DataDisplayResponse(
392
-			$formattedTextResponse,
393
-			Http::STATUS_OK,
394
-			[
395
-				'Content-Type' => 'text/plain',
396
-			]
397
-		);
398
-
399
-		return $response;
400
-	}
401
-
402
-	/**
403
-	 * Checks whether a PHP opcache is properly set up
404
-	 * @return bool
405
-	 */
406
-	protected function isOpcacheProperlySetup() {
407
-		$iniWrapper = new IniGetWrapper();
408
-
409
-		if(!$iniWrapper->getBool('opcache.enable')) {
410
-			return false;
411
-		}
412
-
413
-		if(!$iniWrapper->getBool('opcache.save_comments')) {
414
-			return false;
415
-		}
416
-
417
-		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
418
-			return false;
419
-		}
420
-
421
-		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
422
-			return false;
423
-		}
424
-
425
-		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
426
-			return false;
427
-		}
428
-
429
-		return true;
430
-	}
431
-
432
-	/**
433
-	 * Check if the required FreeType functions are present
434
-	 * @return bool
435
-	 */
436
-	protected function hasFreeTypeSupport() {
437
-		return function_exists('imagettfbbox') && function_exists('imagettftext');
438
-	}
439
-
440
-	protected function hasMissingIndexes(): array {
441
-		$indexInfo = new MissingIndexInformation();
442
-		// Dispatch event so apps can also hint for pending index updates if needed
443
-		$event = new GenericEvent($indexInfo);
444
-		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
445
-
446
-		return $indexInfo->getListOfMissingIndexes();
447
-	}
448
-
449
-	protected function hasMissingColumns(): array {
450
-		$indexInfo = new MissingColumnInformation();
451
-		// Dispatch event so apps can also hint for pending index updates if needed
452
-		$event = new GenericEvent($indexInfo);
453
-		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
454
-
455
-		return $indexInfo->getListOfMissingColumns();
456
-	}
457
-
458
-	protected function isSqliteUsed() {
459
-		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
460
-	}
461
-
462
-	protected function isReadOnlyConfig(): bool {
463
-		return \OC_Helper::isReadOnlyConfigEnabled();
464
-	}
465
-
466
-	protected function hasValidTransactionIsolationLevel(): bool {
467
-		try {
468
-			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
469
-				return true;
470
-			}
471
-
472
-			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
473
-		} catch (DBALException $e) {
474
-			// ignore
475
-		}
476
-
477
-		return true;
478
-	}
479
-
480
-	protected function hasFileinfoInstalled(): bool {
481
-		return \OC_Util::fileInfoLoaded();
482
-	}
483
-
484
-	protected function hasWorkingFileLocking(): bool {
485
-		return !($this->lockingProvider instanceof NoopLockingProvider);
486
-	}
487
-
488
-	protected function getSuggestedOverwriteCliURL(): string {
489
-		$suggestedOverwriteCliUrl = '';
490
-		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
491
-			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
492
-			if (!$this->config->getSystemValue('config_is_read_only', false)) {
493
-				// Set the overwrite URL when it was not set yet.
494
-				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
495
-				$suggestedOverwriteCliUrl = '';
496
-			}
497
-		}
498
-		return $suggestedOverwriteCliUrl;
499
-	}
500
-
501
-	protected function getLastCronInfo(): array {
502
-		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
503
-		return [
504
-			'diffInSeconds' => time() - $lastCronRun,
505
-			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
506
-			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
507
-		];
508
-	}
509
-
510
-	protected function getCronErrors() {
511
-		$errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
512
-
513
-		if (is_array($errors)) {
514
-			return $errors;
515
-		}
516
-
517
-		return [];
518
-	}
519
-
520
-	protected function isPHPMailerUsed(): bool {
521
-		return $this->config->getSystemValue('mail_smtpmode', 'smtp') === 'php';
522
-	}
523
-
524
-	protected function hasOpcacheLoaded(): bool {
525
-		return function_exists('opcache_get_status');
526
-	}
527
-
528
-	/**
529
-	 * Iterates through the configured app roots and
530
-	 * tests if the subdirectories are owned by the same user than the current user.
531
-	 *
532
-	 * @return array
533
-	 */
534
-	protected function getAppDirsWithDifferentOwner(): array {
535
-		$currentUser = posix_getuid();
536
-		$appDirsWithDifferentOwner = [[]];
537
-
538
-		foreach (OC::$APPSROOTS as $appRoot) {
539
-			if ($appRoot['writable'] === true) {
540
-				$appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
541
-			}
542
-		}
543
-
544
-		$appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
545
-		sort($appDirsWithDifferentOwner);
546
-
547
-		return $appDirsWithDifferentOwner;
548
-	}
549
-
550
-	/**
551
-	 * Tests if the directories for one apps directory are writable by the current user.
552
-	 *
553
-	 * @param int $currentUser The current user
554
-	 * @param array $appRoot The app root config
555
-	 * @return string[] The none writable directory paths inside the app root
556
-	 */
557
-	private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
558
-		$appDirsWithDifferentOwner = [];
559
-		$appsPath = $appRoot['path'];
560
-		$appsDir = new DirectoryIterator($appRoot['path']);
561
-
562
-		foreach ($appsDir as $fileInfo) {
563
-			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
564
-				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
565
-				$appDirUser = fileowner($absAppPath);
566
-				if ($appDirUser !== $currentUser) {
567
-					$appDirsWithDifferentOwner[] = $absAppPath;
568
-				}
569
-			}
570
-		}
571
-
572
-		return $appDirsWithDifferentOwner;
573
-	}
574
-
575
-	/**
576
-	 * Checks for potential PHP modules that would improve the instance
577
-	 *
578
-	 * @return string[] A list of PHP modules that is recommended
579
-	 */
580
-	protected function hasRecommendedPHPModules(): array {
581
-		$recommendedPHPModules = [];
582
-
583
-		if (!extension_loaded('intl')) {
584
-			$recommendedPHPModules[] = 'intl';
585
-		}
586
-
587
-		if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
588
-			if (!extension_loaded('imagick')) {
589
-				$recommendedPHPModules[] = 'imagick';
590
-			}
591
-		}
592
-
593
-		return $recommendedPHPModules;
594
-	}
595
-
596
-	protected function isMysqlUsedWithoutUTF8MB4(): bool {
597
-		return ($this->config->getSystemValue('dbtype', 'sqlite') === 'mysql') && ($this->config->getSystemValue('mysql.utf8mb4', false) === false);
598
-	}
599
-
600
-	protected function hasBigIntConversionPendingColumns(): array {
601
-		// copy of ConvertFilecacheBigInt::getColumnsByTable()
602
-		$tables = [
603
-			'activity' => ['activity_id', 'object_id'],
604
-			'activity_mq' => ['mail_id'],
605
-			'authtoken' => ['id'],
606
-			'bruteforce_attempts' => ['id'],
607
-			'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
608
-			'file_locks' => ['id'],
609
-			'jobs' => ['id'],
610
-			'mimetypes' => ['id'],
611
-			'mounts' => ['id', 'storage_id', 'root_id', 'mount_id'],
612
-			'storages' => ['numeric_id'],
613
-		];
614
-
615
-		$schema = new SchemaWrapper($this->db);
616
-		$isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
617
-		$pendingColumns = [];
618
-
619
-		foreach ($tables as $tableName => $columns) {
620
-			if (!$schema->hasTable($tableName)) {
621
-				continue;
622
-			}
623
-
624
-			$table = $schema->getTable($tableName);
625
-			foreach ($columns as $columnName) {
626
-				$column = $table->getColumn($columnName);
627
-				$isAutoIncrement = $column->getAutoincrement();
628
-				$isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
629
-				if ($column->getType()->getName() !== Type::BIGINT && !$isAutoIncrementOnSqlite) {
630
-					$pendingColumns[] = $tableName . '.' . $columnName;
631
-				}
632
-			}
633
-		}
634
-
635
-		return $pendingColumns;
636
-	}
637
-
638
-	protected function isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(): bool {
639
-		$objectStore = $this->config->getSystemValue('objectstore', null);
640
-		$objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
641
-
642
-		if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
643
-			return true;
644
-		}
645
-
646
-		if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
647
-			return true;
648
-		}
649
-
650
-		if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
651
-			return true;
652
-		}
653
-
654
-		$tempPath = sys_get_temp_dir();
655
-		if (!is_dir($tempPath)) {
656
-			$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
657
-			return false;
658
-		}
659
-		$freeSpaceInTemp = disk_free_space($tempPath);
660
-		if ($freeSpaceInTemp === false) {
661
-			$this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
662
-			return false;
663
-		}
664
-
665
-		$freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
666
-		if ($freeSpaceInTempInGB > 50) {
667
-			return true;
668
-		}
669
-
670
-		$this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
671
-		return false;
672
-	}
673
-
674
-	/**
675
-	 * @return DataResponse
676
-	 */
677
-	public function check() {
678
-		return new DataResponse(
679
-			[
680
-				'isGetenvServerWorking' => !empty(getenv('PATH')),
681
-				'isReadOnlyConfig' => $this->isReadOnlyConfig(),
682
-				'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
683
-				'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
684
-				'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
685
-				'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
686
-				'cronInfo' => $this->getLastCronInfo(),
687
-				'cronErrors' => $this->getCronErrors(),
688
-				'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(),
689
-				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
690
-				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
691
-				'isRandomnessSecure' => $this->isRandomnessSecure(),
692
-				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
693
-				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
694
-				'phpSupported' => $this->isPhpSupported(),
695
-				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
696
-				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
697
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
698
-				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
699
-				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
700
-				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
701
-				'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
702
-				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
703
-				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
704
-				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
705
-				'missingIndexes' => $this->hasMissingIndexes(),
706
-				'missingColumns' => $this->hasMissingColumns(),
707
-				'isSqliteUsed' => $this->isSqliteUsed(),
708
-				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
709
-				'isPHPMailerUsed' => $this->isPHPMailerUsed(),
710
-				'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
711
-				'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
712
-				'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
713
-				'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
714
-				'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
715
-				'isMysqlUsedWithoutUTF8MB4' => $this->isMysqlUsedWithoutUTF8MB4(),
716
-				'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => $this->isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(),
717
-				'reverseProxyGeneratedURL' => $this->urlGenerator->getAbsoluteURL('index.php'),
718
-			]
719
-		);
720
-	}
385
+            $formattedTextResponse .= print_r($completeResults, true);
386
+        } else {
387
+            $formattedTextResponse = 'No errors have been found.';
388
+        }
389
+
390
+
391
+        $response = new DataDisplayResponse(
392
+            $formattedTextResponse,
393
+            Http::STATUS_OK,
394
+            [
395
+                'Content-Type' => 'text/plain',
396
+            ]
397
+        );
398
+
399
+        return $response;
400
+    }
401
+
402
+    /**
403
+     * Checks whether a PHP opcache is properly set up
404
+     * @return bool
405
+     */
406
+    protected function isOpcacheProperlySetup() {
407
+        $iniWrapper = new IniGetWrapper();
408
+
409
+        if(!$iniWrapper->getBool('opcache.enable')) {
410
+            return false;
411
+        }
412
+
413
+        if(!$iniWrapper->getBool('opcache.save_comments')) {
414
+            return false;
415
+        }
416
+
417
+        if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
418
+            return false;
419
+        }
420
+
421
+        if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
422
+            return false;
423
+        }
424
+
425
+        if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
426
+            return false;
427
+        }
428
+
429
+        return true;
430
+    }
431
+
432
+    /**
433
+     * Check if the required FreeType functions are present
434
+     * @return bool
435
+     */
436
+    protected function hasFreeTypeSupport() {
437
+        return function_exists('imagettfbbox') && function_exists('imagettftext');
438
+    }
439
+
440
+    protected function hasMissingIndexes(): array {
441
+        $indexInfo = new MissingIndexInformation();
442
+        // Dispatch event so apps can also hint for pending index updates if needed
443
+        $event = new GenericEvent($indexInfo);
444
+        $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
445
+
446
+        return $indexInfo->getListOfMissingIndexes();
447
+    }
448
+
449
+    protected function hasMissingColumns(): array {
450
+        $indexInfo = new MissingColumnInformation();
451
+        // Dispatch event so apps can also hint for pending index updates if needed
452
+        $event = new GenericEvent($indexInfo);
453
+        $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
454
+
455
+        return $indexInfo->getListOfMissingColumns();
456
+    }
457
+
458
+    protected function isSqliteUsed() {
459
+        return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
460
+    }
461
+
462
+    protected function isReadOnlyConfig(): bool {
463
+        return \OC_Helper::isReadOnlyConfigEnabled();
464
+    }
465
+
466
+    protected function hasValidTransactionIsolationLevel(): bool {
467
+        try {
468
+            if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
469
+                return true;
470
+            }
471
+
472
+            return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
473
+        } catch (DBALException $e) {
474
+            // ignore
475
+        }
476
+
477
+        return true;
478
+    }
479
+
480
+    protected function hasFileinfoInstalled(): bool {
481
+        return \OC_Util::fileInfoLoaded();
482
+    }
483
+
484
+    protected function hasWorkingFileLocking(): bool {
485
+        return !($this->lockingProvider instanceof NoopLockingProvider);
486
+    }
487
+
488
+    protected function getSuggestedOverwriteCliURL(): string {
489
+        $suggestedOverwriteCliUrl = '';
490
+        if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
491
+            $suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
492
+            if (!$this->config->getSystemValue('config_is_read_only', false)) {
493
+                // Set the overwrite URL when it was not set yet.
494
+                $this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
495
+                $suggestedOverwriteCliUrl = '';
496
+            }
497
+        }
498
+        return $suggestedOverwriteCliUrl;
499
+    }
500
+
501
+    protected function getLastCronInfo(): array {
502
+        $lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
503
+        return [
504
+            'diffInSeconds' => time() - $lastCronRun,
505
+            'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
506
+            'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
507
+        ];
508
+    }
509
+
510
+    protected function getCronErrors() {
511
+        $errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
512
+
513
+        if (is_array($errors)) {
514
+            return $errors;
515
+        }
516
+
517
+        return [];
518
+    }
519
+
520
+    protected function isPHPMailerUsed(): bool {
521
+        return $this->config->getSystemValue('mail_smtpmode', 'smtp') === 'php';
522
+    }
523
+
524
+    protected function hasOpcacheLoaded(): bool {
525
+        return function_exists('opcache_get_status');
526
+    }
527
+
528
+    /**
529
+     * Iterates through the configured app roots and
530
+     * tests if the subdirectories are owned by the same user than the current user.
531
+     *
532
+     * @return array
533
+     */
534
+    protected function getAppDirsWithDifferentOwner(): array {
535
+        $currentUser = posix_getuid();
536
+        $appDirsWithDifferentOwner = [[]];
537
+
538
+        foreach (OC::$APPSROOTS as $appRoot) {
539
+            if ($appRoot['writable'] === true) {
540
+                $appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
541
+            }
542
+        }
543
+
544
+        $appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
545
+        sort($appDirsWithDifferentOwner);
546
+
547
+        return $appDirsWithDifferentOwner;
548
+    }
549
+
550
+    /**
551
+     * Tests if the directories for one apps directory are writable by the current user.
552
+     *
553
+     * @param int $currentUser The current user
554
+     * @param array $appRoot The app root config
555
+     * @return string[] The none writable directory paths inside the app root
556
+     */
557
+    private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
558
+        $appDirsWithDifferentOwner = [];
559
+        $appsPath = $appRoot['path'];
560
+        $appsDir = new DirectoryIterator($appRoot['path']);
561
+
562
+        foreach ($appsDir as $fileInfo) {
563
+            if ($fileInfo->isDir() && !$fileInfo->isDot()) {
564
+                $absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
565
+                $appDirUser = fileowner($absAppPath);
566
+                if ($appDirUser !== $currentUser) {
567
+                    $appDirsWithDifferentOwner[] = $absAppPath;
568
+                }
569
+            }
570
+        }
571
+
572
+        return $appDirsWithDifferentOwner;
573
+    }
574
+
575
+    /**
576
+     * Checks for potential PHP modules that would improve the instance
577
+     *
578
+     * @return string[] A list of PHP modules that is recommended
579
+     */
580
+    protected function hasRecommendedPHPModules(): array {
581
+        $recommendedPHPModules = [];
582
+
583
+        if (!extension_loaded('intl')) {
584
+            $recommendedPHPModules[] = 'intl';
585
+        }
586
+
587
+        if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
588
+            if (!extension_loaded('imagick')) {
589
+                $recommendedPHPModules[] = 'imagick';
590
+            }
591
+        }
592
+
593
+        return $recommendedPHPModules;
594
+    }
595
+
596
+    protected function isMysqlUsedWithoutUTF8MB4(): bool {
597
+        return ($this->config->getSystemValue('dbtype', 'sqlite') === 'mysql') && ($this->config->getSystemValue('mysql.utf8mb4', false) === false);
598
+    }
599
+
600
+    protected function hasBigIntConversionPendingColumns(): array {
601
+        // copy of ConvertFilecacheBigInt::getColumnsByTable()
602
+        $tables = [
603
+            'activity' => ['activity_id', 'object_id'],
604
+            'activity_mq' => ['mail_id'],
605
+            'authtoken' => ['id'],
606
+            'bruteforce_attempts' => ['id'],
607
+            'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
608
+            'file_locks' => ['id'],
609
+            'jobs' => ['id'],
610
+            'mimetypes' => ['id'],
611
+            'mounts' => ['id', 'storage_id', 'root_id', 'mount_id'],
612
+            'storages' => ['numeric_id'],
613
+        ];
614
+
615
+        $schema = new SchemaWrapper($this->db);
616
+        $isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
617
+        $pendingColumns = [];
618
+
619
+        foreach ($tables as $tableName => $columns) {
620
+            if (!$schema->hasTable($tableName)) {
621
+                continue;
622
+            }
623
+
624
+            $table = $schema->getTable($tableName);
625
+            foreach ($columns as $columnName) {
626
+                $column = $table->getColumn($columnName);
627
+                $isAutoIncrement = $column->getAutoincrement();
628
+                $isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
629
+                if ($column->getType()->getName() !== Type::BIGINT && !$isAutoIncrementOnSqlite) {
630
+                    $pendingColumns[] = $tableName . '.' . $columnName;
631
+                }
632
+            }
633
+        }
634
+
635
+        return $pendingColumns;
636
+    }
637
+
638
+    protected function isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(): bool {
639
+        $objectStore = $this->config->getSystemValue('objectstore', null);
640
+        $objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
641
+
642
+        if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
643
+            return true;
644
+        }
645
+
646
+        if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
647
+            return true;
648
+        }
649
+
650
+        if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
651
+            return true;
652
+        }
653
+
654
+        $tempPath = sys_get_temp_dir();
655
+        if (!is_dir($tempPath)) {
656
+            $this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
657
+            return false;
658
+        }
659
+        $freeSpaceInTemp = disk_free_space($tempPath);
660
+        if ($freeSpaceInTemp === false) {
661
+            $this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
662
+            return false;
663
+        }
664
+
665
+        $freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
666
+        if ($freeSpaceInTempInGB > 50) {
667
+            return true;
668
+        }
669
+
670
+        $this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
671
+        return false;
672
+    }
673
+
674
+    /**
675
+     * @return DataResponse
676
+     */
677
+    public function check() {
678
+        return new DataResponse(
679
+            [
680
+                'isGetenvServerWorking' => !empty(getenv('PATH')),
681
+                'isReadOnlyConfig' => $this->isReadOnlyConfig(),
682
+                'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
683
+                'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
684
+                'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
685
+                'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
686
+                'cronInfo' => $this->getLastCronInfo(),
687
+                'cronErrors' => $this->getCronErrors(),
688
+                'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(),
689
+                'isMemcacheConfigured' => $this->isMemcacheConfigured(),
690
+                'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
691
+                'isRandomnessSecure' => $this->isRandomnessSecure(),
692
+                'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
693
+                'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
694
+                'phpSupported' => $this->isPhpSupported(),
695
+                'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
696
+                'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
697
+                'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
698
+                'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
699
+                'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
700
+                'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
701
+                'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
702
+                'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
703
+                'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
704
+                'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
705
+                'missingIndexes' => $this->hasMissingIndexes(),
706
+                'missingColumns' => $this->hasMissingColumns(),
707
+                'isSqliteUsed' => $this->isSqliteUsed(),
708
+                'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
709
+                'isPHPMailerUsed' => $this->isPHPMailerUsed(),
710
+                'mailSettingsDocumentation' => $this->urlGenerator->getAbsoluteURL('index.php/settings/admin'),
711
+                'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
712
+                'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
713
+                'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
714
+                'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
715
+                'isMysqlUsedWithoutUTF8MB4' => $this->isMysqlUsedWithoutUTF8MB4(),
716
+                'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => $this->isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(),
717
+                'reverseProxyGeneratedURL' => $this->urlGenerator->getAbsoluteURL('index.php'),
718
+            ]
719
+        );
720
+    }
721 721
 }
Please login to merge, or discard this patch.
Spacing   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -137,7 +137,7 @@  discard block
 block discarded – undo
137 137
 			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
138 138
 		]);
139 139
 
140
-		foreach($siteArray as $site) {
140
+		foreach ($siteArray as $site) {
141 141
 			if ($this->isSiteReachable($site)) {
142 142
 				return false;
143 143
 			}
@@ -150,8 +150,8 @@  discard block
 block discarded – undo
150 150
 	 * @return bool
151 151
 	 */
152 152
 	private function isSiteReachable($sitename) {
153
-		$httpSiteName = 'http://' . $sitename . '/';
154
-		$httpsSiteName = 'https://' . $sitename . '/';
153
+		$httpSiteName = 'http://'.$sitename.'/';
154
+		$httpsSiteName = 'https://'.$sitename.'/';
155 155
 
156 156
 		try {
157 157
 			$client = $this->clientService->newClient();
@@ -208,40 +208,40 @@  discard block
 block discarded – undo
208 208
 		// Don't run check when:
209 209
 		// 1. Server has `has_internet_connection` set to false
210 210
 		// 2. AppStore AND S2S is disabled
211
-		if(!$this->config->getSystemValue('has_internet_connection', true)) {
211
+		if (!$this->config->getSystemValue('has_internet_connection', true)) {
212 212
 			return '';
213 213
 		}
214
-		if(!$this->config->getSystemValue('appstoreenabled', true)
214
+		if (!$this->config->getSystemValue('appstoreenabled', true)
215 215
 			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
216 216
 			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
217 217
 			return '';
218 218
 		}
219 219
 
220 220
 		$versionString = $this->getCurlVersion();
221
-		if(isset($versionString['ssl_version'])) {
221
+		if (isset($versionString['ssl_version'])) {
222 222
 			$versionString = $versionString['ssl_version'];
223 223
 		} else {
224 224
 			return '';
225 225
 		}
226 226
 
227
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
228
-		if(!$this->config->getSystemValue('appstoreenabled', true)) {
229
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
227
+		$features = (string) $this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
228
+		if (!$this->config->getSystemValue('appstoreenabled', true)) {
229
+			$features = (string) $this->l10n->t('Federated Cloud Sharing');
230 230
 		}
231 231
 
232 232
 		// Check if at least OpenSSL after 1.01d or 1.0.2b
233
-		if(strpos($versionString, 'OpenSSL/') === 0) {
233
+		if (strpos($versionString, 'OpenSSL/') === 0) {
234 234
 			$majorVersion = substr($versionString, 8, 5);
235 235
 			$patchRelease = substr($versionString, 13, 6);
236 236
 
237
-			if(($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
237
+			if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
238 238
 				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
239 239
 				return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
240 240
 			}
241 241
 		}
242 242
 
243 243
 		// Check if NSS and perform heuristic check
244
-		if(strpos($versionString, 'NSS/') === 0) {
244
+		if (strpos($versionString, 'NSS/') === 0) {
245 245
 			try {
246 246
 				$firstClient = $this->clientService->newClient();
247 247
 				$firstClient->get('https://nextcloud.com/');
@@ -249,7 +249,7 @@  discard block
 block discarded – undo
249 249
 				$secondClient = $this->clientService->newClient();
250 250
 				$secondClient->get('https://nextcloud.com/');
251 251
 			} catch (ClientException $e) {
252
-				if($e->getResponse()->getStatusCode() === 400) {
252
+				if ($e->getResponse()->getStatusCode() === 400) {
253 253
 					return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
254 254
 				}
255 255
 			}
@@ -344,13 +344,13 @@  discard block
 block discarded – undo
344 344
 	 * @return DataResponse
345 345
 	 */
346 346
 	public function getFailedIntegrityCheckFiles() {
347
-		if(!$this->checker->isCodeCheckEnforced()) {
347
+		if (!$this->checker->isCodeCheckEnforced()) {
348 348
 			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
349 349
 		}
350 350
 
351 351
 		$completeResults = $this->checker->getResults();
352 352
 
353
-		if(!empty($completeResults)) {
353
+		if (!empty($completeResults)) {
354 354
 			$formattedTextResponse = 'Technical information
355 355
 =====================
356 356
 The following list covers which files have failed the integrity check. Please read
@@ -360,12 +360,12 @@  discard block
 block discarded – undo
360 360
 Results
361 361
 =======
362 362
 ';
363
-			foreach($completeResults as $context => $contextResult) {
363
+			foreach ($completeResults as $context => $contextResult) {
364 364
 				$formattedTextResponse .= "- $context\n";
365 365
 
366
-				foreach($contextResult as $category => $result) {
366
+				foreach ($contextResult as $category => $result) {
367 367
 					$formattedTextResponse .= "\t- $category\n";
368
-					if($category !== 'EXCEPTION') {
368
+					if ($category !== 'EXCEPTION') {
369 369
 						foreach ($result as $key => $results) {
370 370
 							$formattedTextResponse .= "\t\t- $key\n";
371 371
 						}
@@ -406,23 +406,23 @@  discard block
 block discarded – undo
406 406
 	protected function isOpcacheProperlySetup() {
407 407
 		$iniWrapper = new IniGetWrapper();
408 408
 
409
-		if(!$iniWrapper->getBool('opcache.enable')) {
409
+		if (!$iniWrapper->getBool('opcache.enable')) {
410 410
 			return false;
411 411
 		}
412 412
 
413
-		if(!$iniWrapper->getBool('opcache.save_comments')) {
413
+		if (!$iniWrapper->getBool('opcache.save_comments')) {
414 414
 			return false;
415 415
 		}
416 416
 
417
-		if($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
417
+		if ($iniWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
418 418
 			return false;
419 419
 		}
420 420
 
421
-		if($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
421
+		if ($iniWrapper->getNumeric('opcache.memory_consumption') < 128) {
422 422
 			return false;
423 423
 		}
424 424
 
425
-		if($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
425
+		if ($iniWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
426 426
 			return false;
427 427
 		}
428 428
 
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
 	protected function getSuggestedOverwriteCliURL(): string {
489 489
 		$suggestedOverwriteCliUrl = '';
490 490
 		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
491
-			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
491
+			$suggestedOverwriteCliUrl = $this->request->getServerProtocol().'://'.$this->request->getInsecureServerHost().\OC::$WEBROOT;
492 492
 			if (!$this->config->getSystemValue('config_is_read_only', false)) {
493 493
 				// Set the overwrite URL when it was not set yet.
494 494
 				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
@@ -503,7 +503,7 @@  discard block
 block discarded – undo
503 503
 		return [
504 504
 			'diffInSeconds' => time() - $lastCronRun,
505 505
 			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
506
-			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
506
+			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']).'#backgroundjobs',
507 507
 		];
508 508
 	}
509 509
 
@@ -561,7 +561,7 @@  discard block
 block discarded – undo
561 561
 
562 562
 		foreach ($appsDir as $fileInfo) {
563 563
 			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
564
-				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
564
+				$absAppPath = $appsPath.DIRECTORY_SEPARATOR.$fileInfo->getFilename();
565 565
 				$appDirUser = fileowner($absAppPath);
566 566
 				if ($appDirUser !== $currentUser) {
567 567
 					$appDirsWithDifferentOwner[] = $absAppPath;
@@ -627,7 +627,7 @@  discard block
 block discarded – undo
627 627
 				$isAutoIncrement = $column->getAutoincrement();
628 628
 				$isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
629 629
 				if ($column->getType()->getName() !== Type::BIGINT && !$isAutoIncrementOnSqlite) {
630
-					$pendingColumns[] = $tableName . '.' . $columnName;
630
+					$pendingColumns[] = $tableName.'.'.$columnName;
631 631
 				}
632 632
 			}
633 633
 		}
@@ -653,12 +653,12 @@  discard block
 block discarded – undo
653 653
 
654 654
 		$tempPath = sys_get_temp_dir();
655 655
 		if (!is_dir($tempPath)) {
656
-			$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
656
+			$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: '.$tempPath);
657 657
 			return false;
658 658
 		}
659 659
 		$freeSpaceInTemp = disk_free_space($tempPath);
660 660
 		if ($freeSpaceInTemp === false) {
661
-			$this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
661
+			$this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: '.$tempPath);
662 662
 			return false;
663 663
 		}
664 664
 
@@ -667,7 +667,7 @@  discard block
 block discarded – undo
667 667
 			return true;
668 668
 		}
669 669
 
670
-		$this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
670
+		$this->logger->warning('Checking the available space in the temporary path resulted in '.round($freeSpaceInTempInGB, 1).' GB instead of the recommended 50GB. Path: '.$tempPath);
671 671
 		return false;
672 672
 	}
673 673
 
Please login to merge, or discard this patch.
lib/public/App.php 2 patches
Indentation   +55 added lines, -55 removed lines patch added patch discarded remove patch
@@ -48,63 +48,63 @@
 block discarded – undo
48 48
 class App {
49 49
 
50 50
 
51
-	/**
52
-	 * Register a Configuration Screen that should appear in the personal settings section.
53
-	 * @param string $app appid
54
-	 * @param string $page page to be included
55
-	 * @return void
56
-	 * @since 4.0.0
57
-	 * @deprecated 14.0.0 Use settings section in appinfo.xml to register personal admin sections
58
-	 */
59
-	public static function registerPersonal( $app, $page ) {
60
-		\OC_App::registerPersonal( $app, $page );
61
-	}
51
+    /**
52
+     * Register a Configuration Screen that should appear in the personal settings section.
53
+     * @param string $app appid
54
+     * @param string $page page to be included
55
+     * @return void
56
+     * @since 4.0.0
57
+     * @deprecated 14.0.0 Use settings section in appinfo.xml to register personal admin sections
58
+     */
59
+    public static function registerPersonal( $app, $page ) {
60
+        \OC_App::registerPersonal( $app, $page );
61
+    }
62 62
 
63
-	/**
64
-	 * Register a Configuration Screen that should appear in the Admin section.
65
-	 * @param string $app string appid
66
-	 * @param string $page string page to be included
67
-	 * @return void
68
-	 * @since 4.0.0
69
-	 * @deprecated 14.0.0 Use settings section in appinfo.xml to register admin sections
70
-	 */
71
-	public static function registerAdmin( $app, $page ) {
72
-		\OC_App::registerAdmin( $app, $page );
73
-	}
63
+    /**
64
+     * Register a Configuration Screen that should appear in the Admin section.
65
+     * @param string $app string appid
66
+     * @param string $page string page to be included
67
+     * @return void
68
+     * @since 4.0.0
69
+     * @deprecated 14.0.0 Use settings section in appinfo.xml to register admin sections
70
+     */
71
+    public static function registerAdmin( $app, $page ) {
72
+        \OC_App::registerAdmin( $app, $page );
73
+    }
74 74
 
75
-	/**
76
-	 * Read app metadata from the info.xml file
77
-	 * @param string $app id of the app or the path of the info.xml file
78
-	 * @param boolean $path (optional)
79
-	 * @return array|null
80
-	 * @deprecated 14.0.0 ise \OC::$server->getAppManager()->getAppInfo($appId)
81
-	 * @since 4.0.0
82
-	 */
83
-	public static function getAppInfo( $app, $path=false ) {
84
-		return \OC_App::getAppInfo( $app, $path);
85
-	}
75
+    /**
76
+     * Read app metadata from the info.xml file
77
+     * @param string $app id of the app or the path of the info.xml file
78
+     * @param boolean $path (optional)
79
+     * @return array|null
80
+     * @deprecated 14.0.0 ise \OC::$server->getAppManager()->getAppInfo($appId)
81
+     * @since 4.0.0
82
+     */
83
+    public static function getAppInfo( $app, $path=false ) {
84
+        return \OC_App::getAppInfo( $app, $path);
85
+    }
86 86
 
87
-	/**
88
-	 * checks whether or not an app is enabled
89
-	 * @param string $app
90
-	 * @return boolean
91
-	 *
92
-	 * This function checks whether or not an app is enabled.
93
-	 * @since 4.0.0
94
-	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
95
-	 */
96
-	public static function isEnabled( $app ) {
97
-		return \OC::$server->getAppManager()->isEnabledForUser( $app );
98
-	}
87
+    /**
88
+     * checks whether or not an app is enabled
89
+     * @param string $app
90
+     * @return boolean
91
+     *
92
+     * This function checks whether or not an app is enabled.
93
+     * @since 4.0.0
94
+     * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
95
+     */
96
+    public static function isEnabled( $app ) {
97
+        return \OC::$server->getAppManager()->isEnabledForUser( $app );
98
+    }
99 99
 
100
-	/**
101
-	 * Get the last version of the app from appinfo/info.xml
102
-	 * @param string $app
103
-	 * @return string
104
-	 * @since 4.0.0
105
-	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion($appId)
106
-	 */
107
-	public static function getAppVersion( $app ) {
108
-		return \OC::$server->getAppManager()->getAppVersion($app);
109
-	}
100
+    /**
101
+     * Get the last version of the app from appinfo/info.xml
102
+     * @param string $app
103
+     * @return string
104
+     * @since 4.0.0
105
+     * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion($appId)
106
+     */
107
+    public static function getAppVersion( $app ) {
108
+        return \OC::$server->getAppManager()->getAppVersion($app);
109
+    }
110 110
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -56,8 +56,8 @@  discard block
 block discarded – undo
56 56
 	 * @since 4.0.0
57 57
 	 * @deprecated 14.0.0 Use settings section in appinfo.xml to register personal admin sections
58 58
 	 */
59
-	public static function registerPersonal( $app, $page ) {
60
-		\OC_App::registerPersonal( $app, $page );
59
+	public static function registerPersonal($app, $page) {
60
+		\OC_App::registerPersonal($app, $page);
61 61
 	}
62 62
 
63 63
 	/**
@@ -68,8 +68,8 @@  discard block
 block discarded – undo
68 68
 	 * @since 4.0.0
69 69
 	 * @deprecated 14.0.0 Use settings section in appinfo.xml to register admin sections
70 70
 	 */
71
-	public static function registerAdmin( $app, $page ) {
72
-		\OC_App::registerAdmin( $app, $page );
71
+	public static function registerAdmin($app, $page) {
72
+		\OC_App::registerAdmin($app, $page);
73 73
 	}
74 74
 
75 75
 	/**
@@ -80,8 +80,8 @@  discard block
 block discarded – undo
80 80
 	 * @deprecated 14.0.0 ise \OC::$server->getAppManager()->getAppInfo($appId)
81 81
 	 * @since 4.0.0
82 82
 	 */
83
-	public static function getAppInfo( $app, $path=false ) {
84
-		return \OC_App::getAppInfo( $app, $path);
83
+	public static function getAppInfo($app, $path = false) {
84
+		return \OC_App::getAppInfo($app, $path);
85 85
 	}
86 86
 
87 87
 	/**
@@ -93,8 +93,8 @@  discard block
 block discarded – undo
93 93
 	 * @since 4.0.0
94 94
 	 * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
95 95
 	 */
96
-	public static function isEnabled( $app ) {
97
-		return \OC::$server->getAppManager()->isEnabledForUser( $app );
96
+	public static function isEnabled($app) {
97
+		return \OC::$server->getAppManager()->isEnabledForUser($app);
98 98
 	}
99 99
 
100 100
 	/**
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 	 * @since 4.0.0
105 105
 	 * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion($appId)
106 106
 	 */
107
-	public static function getAppVersion( $app ) {
107
+	public static function getAppVersion($app) {
108 108
 		return \OC::$server->getAppManager()->getAppVersion($app);
109 109
 	}
110 110
 }
Please login to merge, or discard this patch.
lib/public/AppFramework/ApiController.php 1 patch
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -59,9 +59,9 @@  discard block
 block discarded – undo
59 59
      */
60 60
     public function __construct($appName,
61 61
                                 IRequest $request,
62
-                                $corsMethods='PUT, POST, GET, DELETE, PATCH',
63
-                                $corsAllowedHeaders='Authorization, Content-Type, Accept',
64
-                                $corsMaxAge=1728000){
62
+                                $corsMethods = 'PUT, POST, GET, DELETE, PATCH',
63
+                                $corsAllowedHeaders = 'Authorization, Content-Type, Accept',
64
+                                $corsMaxAge = 1728000) {
65 65
         parent::__construct($appName, $request);
66 66
         $this->corsMethods = $corsMethods;
67 67
         $this->corsAllowedHeaders = $corsAllowedHeaders;
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
      * @since 7.0.0
80 80
      */
81 81
     public function preflightedCors() {
82
-        if(isset($this->request->server['HTTP_ORIGIN'])) {
82
+        if (isset($this->request->server['HTTP_ORIGIN'])) {
83 83
             $origin = $this->request->server['HTTP_ORIGIN'];
84 84
         } else {
85 85
             $origin = '*';
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
         $response = new Response();
89 89
         $response->addHeader('Access-Control-Allow-Origin', $origin);
90 90
         $response->addHeader('Access-Control-Allow-Methods', $this->corsMethods);
91
-        $response->addHeader('Access-Control-Max-Age', (string)$this->corsMaxAge);
91
+        $response->addHeader('Access-Control-Max-Age', (string) $this->corsMaxAge);
92 92
         $response->addHeader('Access-Control-Allow-Headers', $this->corsAllowedHeaders);
93 93
         $response->addHeader('Access-Control-Allow-Credentials', 'false');
94 94
         return $response;
Please login to merge, or discard this patch.
lib/public/BackgroundJob/Job.php 1 patch
Indentation   +103 added lines, -103 removed lines patch added patch discarded remove patch
@@ -39,107 +39,107 @@
 block discarded – undo
39 39
  */
40 40
 abstract class Job implements IJob {
41 41
 
42
-	/** @var int $id */
43
-	protected $id;
44
-
45
-	/** @var int $lastRun */
46
-	protected $lastRun;
47
-
48
-	/** @var mixed $argument */
49
-	protected $argument;
50
-
51
-	/** @var ITimeFactory */
52
-	protected $time;
53
-
54
-	/**
55
-	 * @since 15.0.0
56
-	 */
57
-	public function __construct(ITimeFactory $time) {
58
-		$this->time = $time;
59
-	}
60
-
61
-	/**
62
-	 * The function to prepare the execution of the job.
63
-	 *
64
-	 *
65
-	 * @param IJobList $jobList
66
-	 * @param ILogger|null $logger
67
-	 *
68
-	 * @since 15.0.0
69
-	 */
70
-	public function execute($jobList, ILogger $logger = null) {
71
-		$jobList->setLastRun($this);
72
-		if ($logger === null) {
73
-			$logger = \OC::$server->getLogger();
74
-		}
75
-
76
-		try {
77
-			$jobStartTime = $this->time->getTime();
78
-			$logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']);
79
-			$this->run($this->argument);
80
-			$timeTaken = $this->time->getTime() - $jobStartTime;
81
-
82
-			$logger->debug('Finished ' . get_class($this) . ' job with ID ' . $this->getId() . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']);
83
-			$jobList->setExecutionTime($this, $timeTaken);
84
-		} catch (\Exception $e) {
85
-			if ($logger) {
86
-				$logger->logException($e, [
87
-					'app' => 'core',
88
-					'message' => 'Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')'
89
-				]);
90
-			}
91
-		}
92
-	}
93
-
94
-	/**
95
-	 * @since 15.0.0
96
-	 */
97
-	final public function setId($id) {
98
-		$this->id = $id;
99
-	}
100
-
101
-	/**
102
-	 * @since 15.0.0
103
-	 */
104
-	final public function setLastRun($lastRun) {
105
-		$this->lastRun = $lastRun;
106
-	}
107
-
108
-	/**
109
-	 * @since 15.0.0
110
-	 */
111
-	public function setArgument($argument) {
112
-		$this->argument = $argument;
113
-	}
114
-
115
-	/**
116
-	 * @since 15.0.0
117
-	 */
118
-	final public function getId(): int {
119
-		return $this->id;
120
-	}
121
-
122
-	/**
123
-	 * @since 15.0.0
124
-	 */
125
-	final public function getLastRun(): int {
126
-		return $this->lastRun;
127
-	}
128
-
129
-	/**
130
-	 * @since 15.0.0
131
-	 */
132
-	public function getArgument() {
133
-		return $this->argument;
134
-	}
135
-
136
-	/**
137
-	 * The actual function that is called to run the job
138
-	 *
139
-	 * @param $argument
140
-	 * @return mixed
141
-	 *
142
-	 * @since 15.0.0
143
-	 */
144
-	abstract protected function run($argument);
42
+    /** @var int $id */
43
+    protected $id;
44
+
45
+    /** @var int $lastRun */
46
+    protected $lastRun;
47
+
48
+    /** @var mixed $argument */
49
+    protected $argument;
50
+
51
+    /** @var ITimeFactory */
52
+    protected $time;
53
+
54
+    /**
55
+     * @since 15.0.0
56
+     */
57
+    public function __construct(ITimeFactory $time) {
58
+        $this->time = $time;
59
+    }
60
+
61
+    /**
62
+     * The function to prepare the execution of the job.
63
+     *
64
+     *
65
+     * @param IJobList $jobList
66
+     * @param ILogger|null $logger
67
+     *
68
+     * @since 15.0.0
69
+     */
70
+    public function execute($jobList, ILogger $logger = null) {
71
+        $jobList->setLastRun($this);
72
+        if ($logger === null) {
73
+            $logger = \OC::$server->getLogger();
74
+        }
75
+
76
+        try {
77
+            $jobStartTime = $this->time->getTime();
78
+            $logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']);
79
+            $this->run($this->argument);
80
+            $timeTaken = $this->time->getTime() - $jobStartTime;
81
+
82
+            $logger->debug('Finished ' . get_class($this) . ' job with ID ' . $this->getId() . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']);
83
+            $jobList->setExecutionTime($this, $timeTaken);
84
+        } catch (\Exception $e) {
85
+            if ($logger) {
86
+                $logger->logException($e, [
87
+                    'app' => 'core',
88
+                    'message' => 'Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')'
89
+                ]);
90
+            }
91
+        }
92
+    }
93
+
94
+    /**
95
+     * @since 15.0.0
96
+     */
97
+    final public function setId($id) {
98
+        $this->id = $id;
99
+    }
100
+
101
+    /**
102
+     * @since 15.0.0
103
+     */
104
+    final public function setLastRun($lastRun) {
105
+        $this->lastRun = $lastRun;
106
+    }
107
+
108
+    /**
109
+     * @since 15.0.0
110
+     */
111
+    public function setArgument($argument) {
112
+        $this->argument = $argument;
113
+    }
114
+
115
+    /**
116
+     * @since 15.0.0
117
+     */
118
+    final public function getId(): int {
119
+        return $this->id;
120
+    }
121
+
122
+    /**
123
+     * @since 15.0.0
124
+     */
125
+    final public function getLastRun(): int {
126
+        return $this->lastRun;
127
+    }
128
+
129
+    /**
130
+     * @since 15.0.0
131
+     */
132
+    public function getArgument() {
133
+        return $this->argument;
134
+    }
135
+
136
+    /**
137
+     * The actual function that is called to run the job
138
+     *
139
+     * @param $argument
140
+     * @return mixed
141
+     *
142
+     * @since 15.0.0
143
+     */
144
+    abstract protected function run($argument);
145 145
 }
Please login to merge, or discard this patch.
lib/public/IUserManager.php 1 patch
Indentation   +141 added lines, -141 removed lines patch added patch discarded remove patch
@@ -49,145 +49,145 @@
 block discarded – undo
49 49
  * @since 8.0.0
50 50
  */
51 51
 interface IUserManager {
52
-		/**
53
-		 * register a user backend
54
-		 *
55
-		 * @param \OCP\UserInterface $backend
56
-		 * @since 8.0.0
57
-		 */
58
-	public function registerBackend($backend);
59
-
60
-	/**
61
-	 * Get the active backends
62
-	 * @return \OCP\UserInterface[]
63
-	 * @since 8.0.0
64
-	 */
65
-	public function getBackends();
66
-
67
-	/**
68
-	 * remove a user backend
69
-	 *
70
-	 * @param \OCP\UserInterface $backend
71
-	 * @since 8.0.0
72
-	 */
73
-	public function removeBackend($backend);
74
-
75
-	/**
76
-	 * remove all user backends
77
-	 * @since 8.0.0
78
-	 */
79
-	public function clearBackends() ;
80
-
81
-	/**
82
-	 * get a user by user id
83
-	 *
84
-	 * @param string $uid
85
-	 * @return \OCP\IUser|null Either the user or null if the specified user does not exist
86
-	 * @since 8.0.0
87
-	 */
88
-	public function get($uid);
89
-
90
-	/**
91
-	 * check if a user exists
92
-	 *
93
-	 * @param string $uid
94
-	 * @return bool
95
-	 * @since 8.0.0
96
-	 */
97
-	public function userExists($uid);
98
-
99
-	/**
100
-	 * Check if the password is valid for the user
101
-	 *
102
-	 * @param string $loginName
103
-	 * @param string $password
104
-	 * @return mixed the User object on success, false otherwise
105
-	 * @since 8.0.0
106
-	 */
107
-	public function checkPassword($loginName, $password);
108
-
109
-	/**
110
-	 * search by user id
111
-	 *
112
-	 * @param string $pattern
113
-	 * @param int $limit
114
-	 * @param int $offset
115
-	 * @return \OCP\IUser[]
116
-	 * @since 8.0.0
117
-	 */
118
-	public function search($pattern, $limit = null, $offset = null);
119
-
120
-	/**
121
-	 * search by displayName
122
-	 *
123
-	 * @param string $pattern
124
-	 * @param int $limit
125
-	 * @param int $offset
126
-	 * @return \OCP\IUser[]
127
-	 * @since 8.0.0
128
-	 */
129
-	public function searchDisplayName($pattern, $limit = null, $offset = null);
130
-
131
-	/**
132
-	 * @param string $uid
133
-	 * @param string $password
134
-	 * @throws \InvalidArgumentException
135
-	 * @return bool|\OCP\IUser the created user or false
136
-	 * @since 8.0.0
137
-	 */
138
-	public function createUser($uid, $password);
139
-
140
-	/**
141
-	 * @param string $uid
142
-	 * @param string $password
143
-	 * @param UserInterface $backend
144
-	 * @return IUser|null
145
-	 * @throws \InvalidArgumentException
146
-	 * @since 12.0.0
147
-	 */
148
-	public function createUserFromBackend($uid, $password, UserInterface $backend);
149
-
150
-	/**
151
-	 * returns how many users per backend exist (if supported by backend)
152
-	 *
153
-	 * @return array an array of backend class as key and count number as value
154
-	 * @since 8.0.0
155
-	 */
156
-	public function countUsers();
157
-
158
-	/**
159
-	 * @param \Closure $callback
160
-	 * @param string $search
161
-	 * @since 9.0.0
162
-	 */
163
-	public function callForAllUsers(\Closure $callback, $search = '');
164
-
165
-	/**
166
-	 * returns how many users have logged in once
167
-	 *
168
-	 * @return int
169
-	 * @since 11.0.0
170
-	 */
171
-	public function countDisabledUsers();
172
-
173
-	/**
174
-	 * returns how many users have logged in once
175
-	 *
176
-	 * @return int
177
-	 * @since 11.0.0
178
-	 */
179
-	public function countSeenUsers();
180
-
181
-	/**
182
-	 * @param \Closure $callback
183
-	 * @since 11.0.0
184
-	 */
185
-	public function callForSeenUsers(\Closure $callback);
186
-
187
-	/**
188
-	 * @param string $email
189
-	 * @return IUser[]
190
-	 * @since 9.1.0
191
-	 */
192
-	public function getByEmail($email);
52
+        /**
53
+         * register a user backend
54
+         *
55
+         * @param \OCP\UserInterface $backend
56
+         * @since 8.0.0
57
+         */
58
+    public function registerBackend($backend);
59
+
60
+    /**
61
+     * Get the active backends
62
+     * @return \OCP\UserInterface[]
63
+     * @since 8.0.0
64
+     */
65
+    public function getBackends();
66
+
67
+    /**
68
+     * remove a user backend
69
+     *
70
+     * @param \OCP\UserInterface $backend
71
+     * @since 8.0.0
72
+     */
73
+    public function removeBackend($backend);
74
+
75
+    /**
76
+     * remove all user backends
77
+     * @since 8.0.0
78
+     */
79
+    public function clearBackends() ;
80
+
81
+    /**
82
+     * get a user by user id
83
+     *
84
+     * @param string $uid
85
+     * @return \OCP\IUser|null Either the user or null if the specified user does not exist
86
+     * @since 8.0.0
87
+     */
88
+    public function get($uid);
89
+
90
+    /**
91
+     * check if a user exists
92
+     *
93
+     * @param string $uid
94
+     * @return bool
95
+     * @since 8.0.0
96
+     */
97
+    public function userExists($uid);
98
+
99
+    /**
100
+     * Check if the password is valid for the user
101
+     *
102
+     * @param string $loginName
103
+     * @param string $password
104
+     * @return mixed the User object on success, false otherwise
105
+     * @since 8.0.0
106
+     */
107
+    public function checkPassword($loginName, $password);
108
+
109
+    /**
110
+     * search by user id
111
+     *
112
+     * @param string $pattern
113
+     * @param int $limit
114
+     * @param int $offset
115
+     * @return \OCP\IUser[]
116
+     * @since 8.0.0
117
+     */
118
+    public function search($pattern, $limit = null, $offset = null);
119
+
120
+    /**
121
+     * search by displayName
122
+     *
123
+     * @param string $pattern
124
+     * @param int $limit
125
+     * @param int $offset
126
+     * @return \OCP\IUser[]
127
+     * @since 8.0.0
128
+     */
129
+    public function searchDisplayName($pattern, $limit = null, $offset = null);
130
+
131
+    /**
132
+     * @param string $uid
133
+     * @param string $password
134
+     * @throws \InvalidArgumentException
135
+     * @return bool|\OCP\IUser the created user or false
136
+     * @since 8.0.0
137
+     */
138
+    public function createUser($uid, $password);
139
+
140
+    /**
141
+     * @param string $uid
142
+     * @param string $password
143
+     * @param UserInterface $backend
144
+     * @return IUser|null
145
+     * @throws \InvalidArgumentException
146
+     * @since 12.0.0
147
+     */
148
+    public function createUserFromBackend($uid, $password, UserInterface $backend);
149
+
150
+    /**
151
+     * returns how many users per backend exist (if supported by backend)
152
+     *
153
+     * @return array an array of backend class as key and count number as value
154
+     * @since 8.0.0
155
+     */
156
+    public function countUsers();
157
+
158
+    /**
159
+     * @param \Closure $callback
160
+     * @param string $search
161
+     * @since 9.0.0
162
+     */
163
+    public function callForAllUsers(\Closure $callback, $search = '');
164
+
165
+    /**
166
+     * returns how many users have logged in once
167
+     *
168
+     * @return int
169
+     * @since 11.0.0
170
+     */
171
+    public function countDisabledUsers();
172
+
173
+    /**
174
+     * returns how many users have logged in once
175
+     *
176
+     * @return int
177
+     * @since 11.0.0
178
+     */
179
+    public function countSeenUsers();
180
+
181
+    /**
182
+     * @param \Closure $callback
183
+     * @since 11.0.0
184
+     */
185
+    public function callForSeenUsers(\Closure $callback);
186
+
187
+    /**
188
+     * @param string $email
189
+     * @return IUser[]
190
+     * @since 9.1.0
191
+     */
192
+    public function getByEmail($email);
193 193
 }
Please login to merge, or discard this patch.
lib/private/DB/MySqlTools.php 1 patch
Indentation   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -30,41 +30,41 @@
 block discarded – undo
30 30
  */
31 31
 class MySqlTools {
32 32
 
33
-	/**
34
-	 * @param Connection $connection
35
-	 * @return bool
36
-	 */
37
-	public function supports4ByteCharset(IDBConnection $connection) {
38
-		$variables = ['innodb_file_per_table' => 'ON'];
39
-		if (!$this->isMariaDBWithLargePrefix($connection)) {
40
-			$variables['innodb_file_format'] = 'Barracuda';
41
-			$variables['innodb_large_prefix'] = 'ON';
42
-		}
33
+    /**
34
+     * @param Connection $connection
35
+     * @return bool
36
+     */
37
+    public function supports4ByteCharset(IDBConnection $connection) {
38
+        $variables = ['innodb_file_per_table' => 'ON'];
39
+        if (!$this->isMariaDBWithLargePrefix($connection)) {
40
+            $variables['innodb_file_format'] = 'Barracuda';
41
+            $variables['innodb_large_prefix'] = 'ON';
42
+        }
43 43
 
44
-		foreach ($variables as $var => $val) {
45
-			$result = $connection->executeQuery("SHOW VARIABLES LIKE '$var'");
46
-			$row = $result->fetch();
47
-			$result->closeCursor();
48
-			if ($row === false) {
49
-				return false;
50
-			}
51
-			if (strcasecmp($row['Value'], $val) !== 0) {
52
-				return false;
53
-			}
54
-		}
55
-		return true;
56
-	}
44
+        foreach ($variables as $var => $val) {
45
+            $result = $connection->executeQuery("SHOW VARIABLES LIKE '$var'");
46
+            $row = $result->fetch();
47
+            $result->closeCursor();
48
+            if ($row === false) {
49
+                return false;
50
+            }
51
+            if (strcasecmp($row['Value'], $val) !== 0) {
52
+                return false;
53
+            }
54
+        }
55
+        return true;
56
+    }
57 57
 
58
-	protected function isMariaDBWithLargePrefix(IDBConnection $connection) {
59
-		$result = $connection->executeQuery('SELECT VERSION()');
60
-		$row = strtolower($result->fetchColumn());
61
-		$result->closeCursor();
58
+    protected function isMariaDBWithLargePrefix(IDBConnection $connection) {
59
+        $result = $connection->executeQuery('SELECT VERSION()');
60
+        $row = strtolower($result->fetchColumn());
61
+        $result->closeCursor();
62 62
 
63
-		if ($row === false) {
64
-			return false;
65
-		}
63
+        if ($row === false) {
64
+            return false;
65
+        }
66 66
 
67
-		return strpos($row, 'maria') && version_compare($row, '10.3', '>=') ||
68
-			strpos($row, 'maria') === false && version_compare($row, '8.0', '>=');
69
-	}
67
+        return strpos($row, 'maria') && version_compare($row, '10.3', '>=') ||
68
+            strpos($row, 'maria') === false && version_compare($row, '8.0', '>=');
69
+    }
70 70
 }
Please login to merge, or discard this patch.