Passed
Push — master ( 078ac9...e21d65 )
by Roeland
11:50 queued 11s
created
lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -48,7 +48,7 @@
 block discarded – undo
48 48
 	public function iLike($x, $y, $type = null) {
49 49
 		$x = $this->helper->quoteColumnName($x);
50 50
 		$y = $this->helper->quoteColumnName($y);
51
-		return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->charset . '_general_ci LIKE', $y);
51
+		return $this->expressionBuilder->comparison($x, ' COLLATE '.$this->charset.'_general_ci LIKE', $y);
52 52
 	}
53 53
 
54 54
 }
Please login to merge, or discard this patch.
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -30,26 +30,26 @@
 block discarded – undo
30 30
 
31 31
 class MySqlExpressionBuilder extends ExpressionBuilder {
32 32
 
33
-	/** @var string */
34
-	protected $charset;
33
+    /** @var string */
34
+    protected $charset;
35 35
 
36
-	/**
37
-	 * @param \OCP\IDBConnection|Connection $connection
38
-	 * @param IQueryBuilder $queryBuilder
39
-	 */
40
-	public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
41
-		parent::__construct($connection, $queryBuilder);
36
+    /**
37
+     * @param \OCP\IDBConnection|Connection $connection
38
+     * @param IQueryBuilder $queryBuilder
39
+     */
40
+    public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
41
+        parent::__construct($connection, $queryBuilder);
42 42
 
43
-		$params = $connection->getParams();
44
-		$this->charset = isset($params['charset']) ? $params['charset'] : 'utf8';
45
-	}
43
+        $params = $connection->getParams();
44
+        $this->charset = isset($params['charset']) ? $params['charset'] : 'utf8';
45
+    }
46 46
 
47
-	/**
48
-	 * @inheritdoc
49
-	 */
50
-	public function iLike($x, $y, $type = null) {
51
-		$x = $this->helper->quoteColumnName($x);
52
-		$y = $this->helper->quoteColumnName($y);
53
-		return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->charset . '_general_ci LIKE', $y);
54
-	}
47
+    /**
48
+     * @inheritdoc
49
+     */
50
+    public function iLike($x, $y, $type = null) {
51
+        $x = $this->helper->quoteColumnName($x);
52
+        $y = $this->helper->quoteColumnName($y);
53
+        return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->charset . '_general_ci LIKE', $y);
54
+    }
55 55
 }
Please login to merge, or discard this patch.
lib/private/User/Manager.php 1 patch
Indentation   +578 added lines, -578 removed lines patch added patch discarded remove patch
@@ -66,582 +66,582 @@
 block discarded – undo
66 66
  * @package OC\User
67 67
  */
68 68
 class Manager extends PublicEmitter implements IUserManager {
69
-	/**
70
-	 * @var \OCP\UserInterface[] $backends
71
-	 */
72
-	private $backends = [];
73
-
74
-	/**
75
-	 * @var \OC\User\User[] $cachedUsers
76
-	 */
77
-	private $cachedUsers = [];
78
-
79
-	/** @var IConfig */
80
-	private $config;
81
-
82
-	/** @var EventDispatcherInterface */
83
-	private $dispatcher;
84
-
85
-	/** @var IEventDispatcher */
86
-	private $eventDispatcher;
87
-
88
-	public function __construct(IConfig $config,
89
-								EventDispatcherInterface $oldDispatcher,
90
-								IEventDispatcher $eventDispatcher) {
91
-		$this->config = $config;
92
-		$this->dispatcher = $oldDispatcher;
93
-		$cachedUsers = &$this->cachedUsers;
94
-		$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
95
-			/** @var \OC\User\User $user */
96
-			unset($cachedUsers[$user->getUID()]);
97
-		});
98
-		$this->eventDispatcher = $eventDispatcher;
99
-	}
100
-
101
-	/**
102
-	 * Get the active backends
103
-	 * @return \OCP\UserInterface[]
104
-	 */
105
-	public function getBackends() {
106
-		return $this->backends;
107
-	}
108
-
109
-	/**
110
-	 * register a user backend
111
-	 *
112
-	 * @param \OCP\UserInterface $backend
113
-	 */
114
-	public function registerBackend($backend) {
115
-		$this->backends[] = $backend;
116
-	}
117
-
118
-	/**
119
-	 * remove a user backend
120
-	 *
121
-	 * @param \OCP\UserInterface $backend
122
-	 */
123
-	public function removeBackend($backend) {
124
-		$this->cachedUsers = [];
125
-		if (($i = array_search($backend, $this->backends)) !== false) {
126
-			unset($this->backends[$i]);
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * remove all user backends
132
-	 */
133
-	public function clearBackends() {
134
-		$this->cachedUsers = [];
135
-		$this->backends = [];
136
-	}
137
-
138
-	/**
139
-	 * get a user by user id
140
-	 *
141
-	 * @param string $uid
142
-	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
143
-	 */
144
-	public function get($uid) {
145
-		if (is_null($uid) || $uid === '' || $uid === false) {
146
-			return null;
147
-		}
148
-		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
149
-			return $this->cachedUsers[$uid];
150
-		}
151
-		foreach ($this->backends as $backend) {
152
-			if ($backend->userExists($uid)) {
153
-				return $this->getUserObject($uid, $backend);
154
-			}
155
-		}
156
-		return null;
157
-	}
158
-
159
-	/**
160
-	 * get or construct the user object
161
-	 *
162
-	 * @param string $uid
163
-	 * @param \OCP\UserInterface $backend
164
-	 * @param bool $cacheUser If false the newly created user object will not be cached
165
-	 * @return \OC\User\User
166
-	 */
167
-	protected function getUserObject($uid, $backend, $cacheUser = true) {
168
-		if ($backend instanceof IGetRealUIDBackend) {
169
-			$uid = $backend->getRealUID($uid);
170
-		}
171
-
172
-		if (isset($this->cachedUsers[$uid])) {
173
-			return $this->cachedUsers[$uid];
174
-		}
175
-
176
-		$user = new User($uid, $backend, $this->dispatcher, $this, $this->config);
177
-		if ($cacheUser) {
178
-			$this->cachedUsers[$uid] = $user;
179
-		}
180
-		return $user;
181
-	}
182
-
183
-	/**
184
-	 * check if a user exists
185
-	 *
186
-	 * @param string $uid
187
-	 * @return bool
188
-	 */
189
-	public function userExists($uid) {
190
-		$user = $this->get($uid);
191
-		return ($user !== null);
192
-	}
193
-
194
-	/**
195
-	 * Check if the password is valid for the user
196
-	 *
197
-	 * @param string $loginName
198
-	 * @param string $password
199
-	 * @return mixed the User object on success, false otherwise
200
-	 */
201
-	public function checkPassword($loginName, $password) {
202
-		$result = $this->checkPasswordNoLogging($loginName, $password);
203
-
204
-		if ($result === false) {
205
-			\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
206
-		}
207
-
208
-		return $result;
209
-	}
210
-
211
-	/**
212
-	 * Check if the password is valid for the user
213
-	 *
214
-	 * @internal
215
-	 * @param string $loginName
216
-	 * @param string $password
217
-	 * @return IUser|false the User object on success, false otherwise
218
-	 */
219
-	public function checkPasswordNoLogging($loginName, $password) {
220
-		$loginName = str_replace("\0", '', $loginName);
221
-		$password = str_replace("\0", '', $password);
222
-
223
-		foreach ($this->backends as $backend) {
224
-			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
225
-				$uid = $backend->checkPassword($loginName, $password);
226
-				if ($uid !== false) {
227
-					return $this->getUserObject($uid, $backend);
228
-				}
229
-			}
230
-		}
231
-
232
-		return false;
233
-	}
234
-
235
-	/**
236
-	 * search by user id
237
-	 *
238
-	 * @param string $pattern
239
-	 * @param int $limit
240
-	 * @param int $offset
241
-	 * @return \OC\User\User[]
242
-	 */
243
-	public function search($pattern, $limit = null, $offset = null) {
244
-		$users = [];
245
-		foreach ($this->backends as $backend) {
246
-			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
247
-			if (is_array($backendUsers)) {
248
-				foreach ($backendUsers as $uid) {
249
-					$users[$uid] = $this->getUserObject($uid, $backend);
250
-				}
251
-			}
252
-		}
253
-
254
-		uasort($users, function ($a, $b) {
255
-			/**
256
-			 * @var \OC\User\User $a
257
-			 * @var \OC\User\User $b
258
-			 */
259
-			return strcasecmp($a->getUID(), $b->getUID());
260
-		});
261
-		return $users;
262
-	}
263
-
264
-	/**
265
-	 * search by displayName
266
-	 *
267
-	 * @param string $pattern
268
-	 * @param int $limit
269
-	 * @param int $offset
270
-	 * @return \OC\User\User[]
271
-	 */
272
-	public function searchDisplayName($pattern, $limit = null, $offset = null) {
273
-		$users = [];
274
-		foreach ($this->backends as $backend) {
275
-			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
276
-			if (is_array($backendUsers)) {
277
-				foreach ($backendUsers as $uid => $displayName) {
278
-					$users[] = $this->getUserObject($uid, $backend);
279
-				}
280
-			}
281
-		}
282
-
283
-		usort($users, function ($a, $b) {
284
-			/**
285
-			 * @var \OC\User\User $a
286
-			 * @var \OC\User\User $b
287
-			 */
288
-			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
289
-		});
290
-		return $users;
291
-	}
292
-
293
-	/**
294
-	 * @param string $uid
295
-	 * @param string $password
296
-	 * @throws \InvalidArgumentException
297
-	 * @return bool|IUser the created user or false
298
-	 */
299
-	public function createUser($uid, $password) {
300
-		$localBackends = [];
301
-		foreach ($this->backends as $backend) {
302
-			if ($backend instanceof Database) {
303
-				// First check if there is another user backend
304
-				$localBackends[] = $backend;
305
-				continue;
306
-			}
307
-
308
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
309
-				return $this->createUserFromBackend($uid, $password, $backend);
310
-			}
311
-		}
312
-
313
-		foreach ($localBackends as $backend) {
314
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
315
-				return $this->createUserFromBackend($uid, $password, $backend);
316
-			}
317
-		}
318
-
319
-		return false;
320
-	}
321
-
322
-	/**
323
-	 * @param string $uid
324
-	 * @param string $password
325
-	 * @param UserInterface $backend
326
-	 * @return IUser|null
327
-	 * @throws \InvalidArgumentException
328
-	 */
329
-	public function createUserFromBackend($uid, $password, UserInterface $backend) {
330
-		$l = \OC::$server->getL10N('lib');
331
-
332
-		// Check the name for bad characters
333
-		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
334
-		if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
335
-			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
336
-				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
337
-		}
338
-
339
-		// No empty username
340
-		if (trim($uid) === '') {
341
-			throw new \InvalidArgumentException($l->t('A valid username must be provided'));
342
-		}
343
-
344
-		// No whitespace at the beginning or at the end
345
-		if (trim($uid) !== $uid) {
346
-			throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
347
-		}
348
-
349
-		// Username only consists of 1 or 2 dots (directory traversal)
350
-		if ($uid === '.' || $uid === '..') {
351
-			throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
352
-		}
353
-
354
-		if (!$this->verifyUid($uid)) {
355
-			throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user'));
356
-		}
357
-
358
-		// No empty password
359
-		if (trim($password) === '') {
360
-			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
361
-		}
362
-
363
-		// Check if user already exists
364
-		if ($this->userExists($uid)) {
365
-			throw new \InvalidArgumentException($l->t('The username is already being used'));
366
-		}
367
-
368
-		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
369
-		$this->eventDispatcher->dispatchTyped(new CreateUserEvent($uid, $password));
370
-		$state = $backend->createUser($uid, $password);
371
-		if ($state === false) {
372
-			throw new \InvalidArgumentException($l->t('Could not create user'));
373
-		}
374
-		$user = $this->getUserObject($uid, $backend);
375
-		if ($user instanceof IUser) {
376
-			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
377
-			$this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
378
-		}
379
-		return $user;
380
-	}
381
-
382
-	/**
383
-	 * returns how many users per backend exist (if supported by backend)
384
-	 *
385
-	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
386
-	 *                entry in the preferences table will be affected
387
-	 * @return array|int an array of backend class as key and count number as value
388
-	 *                if $hasLoggedIn is true only an int is returned
389
-	 */
390
-	public function countUsers($hasLoggedIn = false) {
391
-		if ($hasLoggedIn) {
392
-			return $this->countSeenUsers();
393
-		}
394
-		$userCountStatistics = [];
395
-		foreach ($this->backends as $backend) {
396
-			if ($backend->implementsActions(Backend::COUNT_USERS)) {
397
-				$backendUsers = $backend->countUsers();
398
-				if ($backendUsers !== false) {
399
-					if ($backend instanceof IUserBackend) {
400
-						$name = $backend->getBackendName();
401
-					} else {
402
-						$name = get_class($backend);
403
-					}
404
-					if (isset($userCountStatistics[$name])) {
405
-						$userCountStatistics[$name] += $backendUsers;
406
-					} else {
407
-						$userCountStatistics[$name] = $backendUsers;
408
-					}
409
-				}
410
-			}
411
-		}
412
-		return $userCountStatistics;
413
-	}
414
-
415
-	/**
416
-	 * returns how many users per backend exist in the requested groups (if supported by backend)
417
-	 *
418
-	 * @param IGroup[] $groups an array of gid to search in
419
-	 * @return array|int an array of backend class as key and count number as value
420
-	 *                if $hasLoggedIn is true only an int is returned
421
-	 */
422
-	public function countUsersOfGroups(array $groups) {
423
-		$users = [];
424
-		foreach ($groups as $group) {
425
-			$usersIds = array_map(function ($user) {
426
-				return $user->getUID();
427
-			}, $group->getUsers());
428
-			$users = array_merge($users, $usersIds);
429
-		}
430
-		return count(array_unique($users));
431
-	}
432
-
433
-	/**
434
-	 * The callback is executed for each user on each backend.
435
-	 * If the callback returns false no further users will be retrieved.
436
-	 *
437
-	 * @param \Closure $callback
438
-	 * @param string $search
439
-	 * @param boolean $onlySeen when true only users that have a lastLogin entry
440
-	 *                in the preferences table will be affected
441
-	 * @since 9.0.0
442
-	 */
443
-	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
444
-		if ($onlySeen) {
445
-			$this->callForSeenUsers($callback);
446
-		} else {
447
-			foreach ($this->getBackends() as $backend) {
448
-				$limit = 500;
449
-				$offset = 0;
450
-				do {
451
-					$users = $backend->getUsers($search, $limit, $offset);
452
-					foreach ($users as $uid) {
453
-						if (!$backend->userExists($uid)) {
454
-							continue;
455
-						}
456
-						$user = $this->getUserObject($uid, $backend, false);
457
-						$return = $callback($user);
458
-						if ($return === false) {
459
-							break;
460
-						}
461
-					}
462
-					$offset += $limit;
463
-				} while (count($users) >= $limit);
464
-			}
465
-		}
466
-	}
467
-
468
-	/**
469
-	 * returns how many users are disabled
470
-	 *
471
-	 * @return int
472
-	 * @since 12.0.0
473
-	 */
474
-	public function countDisabledUsers(): int {
475
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
476
-		$queryBuilder->select($queryBuilder->func()->count('*'))
477
-			->from('preferences')
478
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
479
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
480
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
481
-
482
-
483
-		$result = $queryBuilder->execute();
484
-		$count = $result->fetchColumn();
485
-		$result->closeCursor();
486
-
487
-		if ($count !== false) {
488
-			$count = (int)$count;
489
-		} else {
490
-			$count = 0;
491
-		}
492
-
493
-		return $count;
494
-	}
495
-
496
-	/**
497
-	 * returns how many users are disabled in the requested groups
498
-	 *
499
-	 * @param array $groups groupids to search
500
-	 * @return int
501
-	 * @since 14.0.0
502
-	 */
503
-	public function countDisabledUsersOfGroups(array $groups): int {
504
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
505
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')'))
506
-			->from('preferences', 'p')
507
-			->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid'))
508
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
509
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
510
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
511
-			->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
512
-
513
-		$result = $queryBuilder->execute();
514
-		$count = $result->fetchColumn();
515
-		$result->closeCursor();
516
-
517
-		if ($count !== false) {
518
-			$count = (int)$count;
519
-		} else {
520
-			$count = 0;
521
-		}
522
-
523
-		return $count;
524
-	}
525
-
526
-	/**
527
-	 * returns how many users have logged in once
528
-	 *
529
-	 * @return int
530
-	 * @since 11.0.0
531
-	 */
532
-	public function countSeenUsers() {
533
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
534
-		$queryBuilder->select($queryBuilder->func()->count('*'))
535
-			->from('preferences')
536
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
537
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
538
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
539
-
540
-		$query = $queryBuilder->execute();
541
-
542
-		$result = (int)$query->fetchColumn();
543
-		$query->closeCursor();
544
-
545
-		return $result;
546
-	}
547
-
548
-	/**
549
-	 * @param \Closure $callback
550
-	 * @since 11.0.0
551
-	 */
552
-	public function callForSeenUsers(\Closure $callback) {
553
-		$limit = 1000;
554
-		$offset = 0;
555
-		do {
556
-			$userIds = $this->getSeenUserIds($limit, $offset);
557
-			$offset += $limit;
558
-			foreach ($userIds as $userId) {
559
-				foreach ($this->backends as $backend) {
560
-					if ($backend->userExists($userId)) {
561
-						$user = $this->getUserObject($userId, $backend, false);
562
-						$return = $callback($user);
563
-						if ($return === false) {
564
-							return;
565
-						}
566
-						break;
567
-					}
568
-				}
569
-			}
570
-		} while (count($userIds) >= $limit);
571
-	}
572
-
573
-	/**
574
-	 * Getting all userIds that have a listLogin value requires checking the
575
-	 * value in php because on oracle you cannot use a clob in a where clause,
576
-	 * preventing us from doing a not null or length(value) > 0 check.
577
-	 *
578
-	 * @param int $limit
579
-	 * @param int $offset
580
-	 * @return string[] with user ids
581
-	 */
582
-	private function getSeenUserIds($limit = null, $offset = null) {
583
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
584
-		$queryBuilder->select(['userid'])
585
-			->from('preferences')
586
-			->where($queryBuilder->expr()->eq(
587
-				'appid', $queryBuilder->createNamedParameter('login'))
588
-			)
589
-			->andWhere($queryBuilder->expr()->eq(
590
-				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
591
-			)
592
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
593
-			);
594
-
595
-		if ($limit !== null) {
596
-			$queryBuilder->setMaxResults($limit);
597
-		}
598
-		if ($offset !== null) {
599
-			$queryBuilder->setFirstResult($offset);
600
-		}
601
-		$query = $queryBuilder->execute();
602
-		$result = [];
603
-
604
-		while ($row = $query->fetch()) {
605
-			$result[] = $row['userid'];
606
-		}
607
-
608
-		$query->closeCursor();
609
-
610
-		return $result;
611
-	}
612
-
613
-	/**
614
-	 * @param string $email
615
-	 * @return IUser[]
616
-	 * @since 9.1.0
617
-	 */
618
-	public function getByEmail($email) {
619
-		$userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
620
-
621
-		$users = array_map(function ($uid) {
622
-			return $this->get($uid);
623
-		}, $userIds);
624
-
625
-		return array_values(array_filter($users, function ($u) {
626
-			return ($u instanceof IUser);
627
-		}));
628
-	}
629
-
630
-	private function verifyUid(string $uid): bool {
631
-		$appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
632
-
633
-		if (\in_array($uid, [
634
-			'.htaccess',
635
-			'files_external',
636
-			'.ocdata',
637
-			'owncloud.log',
638
-			'nextcloud.log',
639
-			$appdata], true)) {
640
-			return false;
641
-		}
642
-
643
-		$dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
644
-
645
-		return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
646
-	}
69
+    /**
70
+     * @var \OCP\UserInterface[] $backends
71
+     */
72
+    private $backends = [];
73
+
74
+    /**
75
+     * @var \OC\User\User[] $cachedUsers
76
+     */
77
+    private $cachedUsers = [];
78
+
79
+    /** @var IConfig */
80
+    private $config;
81
+
82
+    /** @var EventDispatcherInterface */
83
+    private $dispatcher;
84
+
85
+    /** @var IEventDispatcher */
86
+    private $eventDispatcher;
87
+
88
+    public function __construct(IConfig $config,
89
+                                EventDispatcherInterface $oldDispatcher,
90
+                                IEventDispatcher $eventDispatcher) {
91
+        $this->config = $config;
92
+        $this->dispatcher = $oldDispatcher;
93
+        $cachedUsers = &$this->cachedUsers;
94
+        $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
95
+            /** @var \OC\User\User $user */
96
+            unset($cachedUsers[$user->getUID()]);
97
+        });
98
+        $this->eventDispatcher = $eventDispatcher;
99
+    }
100
+
101
+    /**
102
+     * Get the active backends
103
+     * @return \OCP\UserInterface[]
104
+     */
105
+    public function getBackends() {
106
+        return $this->backends;
107
+    }
108
+
109
+    /**
110
+     * register a user backend
111
+     *
112
+     * @param \OCP\UserInterface $backend
113
+     */
114
+    public function registerBackend($backend) {
115
+        $this->backends[] = $backend;
116
+    }
117
+
118
+    /**
119
+     * remove a user backend
120
+     *
121
+     * @param \OCP\UserInterface $backend
122
+     */
123
+    public function removeBackend($backend) {
124
+        $this->cachedUsers = [];
125
+        if (($i = array_search($backend, $this->backends)) !== false) {
126
+            unset($this->backends[$i]);
127
+        }
128
+    }
129
+
130
+    /**
131
+     * remove all user backends
132
+     */
133
+    public function clearBackends() {
134
+        $this->cachedUsers = [];
135
+        $this->backends = [];
136
+    }
137
+
138
+    /**
139
+     * get a user by user id
140
+     *
141
+     * @param string $uid
142
+     * @return \OC\User\User|null Either the user or null if the specified user does not exist
143
+     */
144
+    public function get($uid) {
145
+        if (is_null($uid) || $uid === '' || $uid === false) {
146
+            return null;
147
+        }
148
+        if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
149
+            return $this->cachedUsers[$uid];
150
+        }
151
+        foreach ($this->backends as $backend) {
152
+            if ($backend->userExists($uid)) {
153
+                return $this->getUserObject($uid, $backend);
154
+            }
155
+        }
156
+        return null;
157
+    }
158
+
159
+    /**
160
+     * get or construct the user object
161
+     *
162
+     * @param string $uid
163
+     * @param \OCP\UserInterface $backend
164
+     * @param bool $cacheUser If false the newly created user object will not be cached
165
+     * @return \OC\User\User
166
+     */
167
+    protected function getUserObject($uid, $backend, $cacheUser = true) {
168
+        if ($backend instanceof IGetRealUIDBackend) {
169
+            $uid = $backend->getRealUID($uid);
170
+        }
171
+
172
+        if (isset($this->cachedUsers[$uid])) {
173
+            return $this->cachedUsers[$uid];
174
+        }
175
+
176
+        $user = new User($uid, $backend, $this->dispatcher, $this, $this->config);
177
+        if ($cacheUser) {
178
+            $this->cachedUsers[$uid] = $user;
179
+        }
180
+        return $user;
181
+    }
182
+
183
+    /**
184
+     * check if a user exists
185
+     *
186
+     * @param string $uid
187
+     * @return bool
188
+     */
189
+    public function userExists($uid) {
190
+        $user = $this->get($uid);
191
+        return ($user !== null);
192
+    }
193
+
194
+    /**
195
+     * Check if the password is valid for the user
196
+     *
197
+     * @param string $loginName
198
+     * @param string $password
199
+     * @return mixed the User object on success, false otherwise
200
+     */
201
+    public function checkPassword($loginName, $password) {
202
+        $result = $this->checkPasswordNoLogging($loginName, $password);
203
+
204
+        if ($result === false) {
205
+            \OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
206
+        }
207
+
208
+        return $result;
209
+    }
210
+
211
+    /**
212
+     * Check if the password is valid for the user
213
+     *
214
+     * @internal
215
+     * @param string $loginName
216
+     * @param string $password
217
+     * @return IUser|false the User object on success, false otherwise
218
+     */
219
+    public function checkPasswordNoLogging($loginName, $password) {
220
+        $loginName = str_replace("\0", '', $loginName);
221
+        $password = str_replace("\0", '', $password);
222
+
223
+        foreach ($this->backends as $backend) {
224
+            if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
225
+                $uid = $backend->checkPassword($loginName, $password);
226
+                if ($uid !== false) {
227
+                    return $this->getUserObject($uid, $backend);
228
+                }
229
+            }
230
+        }
231
+
232
+        return false;
233
+    }
234
+
235
+    /**
236
+     * search by user id
237
+     *
238
+     * @param string $pattern
239
+     * @param int $limit
240
+     * @param int $offset
241
+     * @return \OC\User\User[]
242
+     */
243
+    public function search($pattern, $limit = null, $offset = null) {
244
+        $users = [];
245
+        foreach ($this->backends as $backend) {
246
+            $backendUsers = $backend->getUsers($pattern, $limit, $offset);
247
+            if (is_array($backendUsers)) {
248
+                foreach ($backendUsers as $uid) {
249
+                    $users[$uid] = $this->getUserObject($uid, $backend);
250
+                }
251
+            }
252
+        }
253
+
254
+        uasort($users, function ($a, $b) {
255
+            /**
256
+             * @var \OC\User\User $a
257
+             * @var \OC\User\User $b
258
+             */
259
+            return strcasecmp($a->getUID(), $b->getUID());
260
+        });
261
+        return $users;
262
+    }
263
+
264
+    /**
265
+     * search by displayName
266
+     *
267
+     * @param string $pattern
268
+     * @param int $limit
269
+     * @param int $offset
270
+     * @return \OC\User\User[]
271
+     */
272
+    public function searchDisplayName($pattern, $limit = null, $offset = null) {
273
+        $users = [];
274
+        foreach ($this->backends as $backend) {
275
+            $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
276
+            if (is_array($backendUsers)) {
277
+                foreach ($backendUsers as $uid => $displayName) {
278
+                    $users[] = $this->getUserObject($uid, $backend);
279
+                }
280
+            }
281
+        }
282
+
283
+        usort($users, function ($a, $b) {
284
+            /**
285
+             * @var \OC\User\User $a
286
+             * @var \OC\User\User $b
287
+             */
288
+            return strcasecmp($a->getDisplayName(), $b->getDisplayName());
289
+        });
290
+        return $users;
291
+    }
292
+
293
+    /**
294
+     * @param string $uid
295
+     * @param string $password
296
+     * @throws \InvalidArgumentException
297
+     * @return bool|IUser the created user or false
298
+     */
299
+    public function createUser($uid, $password) {
300
+        $localBackends = [];
301
+        foreach ($this->backends as $backend) {
302
+            if ($backend instanceof Database) {
303
+                // First check if there is another user backend
304
+                $localBackends[] = $backend;
305
+                continue;
306
+            }
307
+
308
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
309
+                return $this->createUserFromBackend($uid, $password, $backend);
310
+            }
311
+        }
312
+
313
+        foreach ($localBackends as $backend) {
314
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
315
+                return $this->createUserFromBackend($uid, $password, $backend);
316
+            }
317
+        }
318
+
319
+        return false;
320
+    }
321
+
322
+    /**
323
+     * @param string $uid
324
+     * @param string $password
325
+     * @param UserInterface $backend
326
+     * @return IUser|null
327
+     * @throws \InvalidArgumentException
328
+     */
329
+    public function createUserFromBackend($uid, $password, UserInterface $backend) {
330
+        $l = \OC::$server->getL10N('lib');
331
+
332
+        // Check the name for bad characters
333
+        // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
334
+        if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) {
335
+            throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
336
+                . ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
337
+        }
338
+
339
+        // No empty username
340
+        if (trim($uid) === '') {
341
+            throw new \InvalidArgumentException($l->t('A valid username must be provided'));
342
+        }
343
+
344
+        // No whitespace at the beginning or at the end
345
+        if (trim($uid) !== $uid) {
346
+            throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
347
+        }
348
+
349
+        // Username only consists of 1 or 2 dots (directory traversal)
350
+        if ($uid === '.' || $uid === '..') {
351
+            throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
352
+        }
353
+
354
+        if (!$this->verifyUid($uid)) {
355
+            throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user'));
356
+        }
357
+
358
+        // No empty password
359
+        if (trim($password) === '') {
360
+            throw new \InvalidArgumentException($l->t('A valid password must be provided'));
361
+        }
362
+
363
+        // Check if user already exists
364
+        if ($this->userExists($uid)) {
365
+            throw new \InvalidArgumentException($l->t('The username is already being used'));
366
+        }
367
+
368
+        $this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
369
+        $this->eventDispatcher->dispatchTyped(new CreateUserEvent($uid, $password));
370
+        $state = $backend->createUser($uid, $password);
371
+        if ($state === false) {
372
+            throw new \InvalidArgumentException($l->t('Could not create user'));
373
+        }
374
+        $user = $this->getUserObject($uid, $backend);
375
+        if ($user instanceof IUser) {
376
+            $this->emit('\OC\User', 'postCreateUser', [$user, $password]);
377
+            $this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
378
+        }
379
+        return $user;
380
+    }
381
+
382
+    /**
383
+     * returns how many users per backend exist (if supported by backend)
384
+     *
385
+     * @param boolean $hasLoggedIn when true only users that have a lastLogin
386
+     *                entry in the preferences table will be affected
387
+     * @return array|int an array of backend class as key and count number as value
388
+     *                if $hasLoggedIn is true only an int is returned
389
+     */
390
+    public function countUsers($hasLoggedIn = false) {
391
+        if ($hasLoggedIn) {
392
+            return $this->countSeenUsers();
393
+        }
394
+        $userCountStatistics = [];
395
+        foreach ($this->backends as $backend) {
396
+            if ($backend->implementsActions(Backend::COUNT_USERS)) {
397
+                $backendUsers = $backend->countUsers();
398
+                if ($backendUsers !== false) {
399
+                    if ($backend instanceof IUserBackend) {
400
+                        $name = $backend->getBackendName();
401
+                    } else {
402
+                        $name = get_class($backend);
403
+                    }
404
+                    if (isset($userCountStatistics[$name])) {
405
+                        $userCountStatistics[$name] += $backendUsers;
406
+                    } else {
407
+                        $userCountStatistics[$name] = $backendUsers;
408
+                    }
409
+                }
410
+            }
411
+        }
412
+        return $userCountStatistics;
413
+    }
414
+
415
+    /**
416
+     * returns how many users per backend exist in the requested groups (if supported by backend)
417
+     *
418
+     * @param IGroup[] $groups an array of gid to search in
419
+     * @return array|int an array of backend class as key and count number as value
420
+     *                if $hasLoggedIn is true only an int is returned
421
+     */
422
+    public function countUsersOfGroups(array $groups) {
423
+        $users = [];
424
+        foreach ($groups as $group) {
425
+            $usersIds = array_map(function ($user) {
426
+                return $user->getUID();
427
+            }, $group->getUsers());
428
+            $users = array_merge($users, $usersIds);
429
+        }
430
+        return count(array_unique($users));
431
+    }
432
+
433
+    /**
434
+     * The callback is executed for each user on each backend.
435
+     * If the callback returns false no further users will be retrieved.
436
+     *
437
+     * @param \Closure $callback
438
+     * @param string $search
439
+     * @param boolean $onlySeen when true only users that have a lastLogin entry
440
+     *                in the preferences table will be affected
441
+     * @since 9.0.0
442
+     */
443
+    public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
444
+        if ($onlySeen) {
445
+            $this->callForSeenUsers($callback);
446
+        } else {
447
+            foreach ($this->getBackends() as $backend) {
448
+                $limit = 500;
449
+                $offset = 0;
450
+                do {
451
+                    $users = $backend->getUsers($search, $limit, $offset);
452
+                    foreach ($users as $uid) {
453
+                        if (!$backend->userExists($uid)) {
454
+                            continue;
455
+                        }
456
+                        $user = $this->getUserObject($uid, $backend, false);
457
+                        $return = $callback($user);
458
+                        if ($return === false) {
459
+                            break;
460
+                        }
461
+                    }
462
+                    $offset += $limit;
463
+                } while (count($users) >= $limit);
464
+            }
465
+        }
466
+    }
467
+
468
+    /**
469
+     * returns how many users are disabled
470
+     *
471
+     * @return int
472
+     * @since 12.0.0
473
+     */
474
+    public function countDisabledUsers(): int {
475
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
476
+        $queryBuilder->select($queryBuilder->func()->count('*'))
477
+            ->from('preferences')
478
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
479
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
480
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR));
481
+
482
+
483
+        $result = $queryBuilder->execute();
484
+        $count = $result->fetchColumn();
485
+        $result->closeCursor();
486
+
487
+        if ($count !== false) {
488
+            $count = (int)$count;
489
+        } else {
490
+            $count = 0;
491
+        }
492
+
493
+        return $count;
494
+    }
495
+
496
+    /**
497
+     * returns how many users are disabled in the requested groups
498
+     *
499
+     * @param array $groups groupids to search
500
+     * @return int
501
+     * @since 14.0.0
502
+     */
503
+    public function countDisabledUsersOfGroups(array $groups): int {
504
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
505
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(DISTINCT ' . $queryBuilder->getColumnName('uid') . ')'))
506
+            ->from('preferences', 'p')
507
+            ->innerJoin('p', 'group_user', 'g', $queryBuilder->expr()->eq('p.userid', 'g.uid'))
508
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
509
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
510
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
511
+            ->andWhere($queryBuilder->expr()->in('gid', $queryBuilder->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
512
+
513
+        $result = $queryBuilder->execute();
514
+        $count = $result->fetchColumn();
515
+        $result->closeCursor();
516
+
517
+        if ($count !== false) {
518
+            $count = (int)$count;
519
+        } else {
520
+            $count = 0;
521
+        }
522
+
523
+        return $count;
524
+    }
525
+
526
+    /**
527
+     * returns how many users have logged in once
528
+     *
529
+     * @return int
530
+     * @since 11.0.0
531
+     */
532
+    public function countSeenUsers() {
533
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
534
+        $queryBuilder->select($queryBuilder->func()->count('*'))
535
+            ->from('preferences')
536
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
537
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
538
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
539
+
540
+        $query = $queryBuilder->execute();
541
+
542
+        $result = (int)$query->fetchColumn();
543
+        $query->closeCursor();
544
+
545
+        return $result;
546
+    }
547
+
548
+    /**
549
+     * @param \Closure $callback
550
+     * @since 11.0.0
551
+     */
552
+    public function callForSeenUsers(\Closure $callback) {
553
+        $limit = 1000;
554
+        $offset = 0;
555
+        do {
556
+            $userIds = $this->getSeenUserIds($limit, $offset);
557
+            $offset += $limit;
558
+            foreach ($userIds as $userId) {
559
+                foreach ($this->backends as $backend) {
560
+                    if ($backend->userExists($userId)) {
561
+                        $user = $this->getUserObject($userId, $backend, false);
562
+                        $return = $callback($user);
563
+                        if ($return === false) {
564
+                            return;
565
+                        }
566
+                        break;
567
+                    }
568
+                }
569
+            }
570
+        } while (count($userIds) >= $limit);
571
+    }
572
+
573
+    /**
574
+     * Getting all userIds that have a listLogin value requires checking the
575
+     * value in php because on oracle you cannot use a clob in a where clause,
576
+     * preventing us from doing a not null or length(value) > 0 check.
577
+     *
578
+     * @param int $limit
579
+     * @param int $offset
580
+     * @return string[] with user ids
581
+     */
582
+    private function getSeenUserIds($limit = null, $offset = null) {
583
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
584
+        $queryBuilder->select(['userid'])
585
+            ->from('preferences')
586
+            ->where($queryBuilder->expr()->eq(
587
+                'appid', $queryBuilder->createNamedParameter('login'))
588
+            )
589
+            ->andWhere($queryBuilder->expr()->eq(
590
+                'configkey', $queryBuilder->createNamedParameter('lastLogin'))
591
+            )
592
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue')
593
+            );
594
+
595
+        if ($limit !== null) {
596
+            $queryBuilder->setMaxResults($limit);
597
+        }
598
+        if ($offset !== null) {
599
+            $queryBuilder->setFirstResult($offset);
600
+        }
601
+        $query = $queryBuilder->execute();
602
+        $result = [];
603
+
604
+        while ($row = $query->fetch()) {
605
+            $result[] = $row['userid'];
606
+        }
607
+
608
+        $query->closeCursor();
609
+
610
+        return $result;
611
+    }
612
+
613
+    /**
614
+     * @param string $email
615
+     * @return IUser[]
616
+     * @since 9.1.0
617
+     */
618
+    public function getByEmail($email) {
619
+        $userIds = $this->config->getUsersForUserValueCaseInsensitive('settings', 'email', $email);
620
+
621
+        $users = array_map(function ($uid) {
622
+            return $this->get($uid);
623
+        }, $userIds);
624
+
625
+        return array_values(array_filter($users, function ($u) {
626
+            return ($u instanceof IUser);
627
+        }));
628
+    }
629
+
630
+    private function verifyUid(string $uid): bool {
631
+        $appdata = 'appdata_' . $this->config->getSystemValueString('instanceid');
632
+
633
+        if (\in_array($uid, [
634
+            '.htaccess',
635
+            'files_external',
636
+            '.ocdata',
637
+            'owncloud.log',
638
+            'nextcloud.log',
639
+            $appdata], true)) {
640
+            return false;
641
+        }
642
+
643
+        $dataDirectory = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data');
644
+
645
+        return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
646
+    }
647 647
 }
Please login to merge, or discard this patch.