Completed
Pull Request — master (#5709)
by Robin
54:52 queued 28:57
created
lib/private/User/Manager.php 1 patch
Indentation   +489 added lines, -489 removed lines patch added patch discarded remove patch
@@ -55,493 +55,493 @@
 block discarded – undo
55 55
  * @package OC\User
56 56
  */
57 57
 class Manager extends PublicEmitter implements IUserManager {
58
-	/**
59
-	 * @var \OCP\UserInterface[] $backends
60
-	 */
61
-	private $backends = array();
62
-
63
-	/**
64
-	 * @var \OC\User\User[] $cachedUsers
65
-	 */
66
-	private $cachedUsers = array();
67
-
68
-	/**
69
-	 * @var \OCP\IConfig $config
70
-	 */
71
-	private $config;
72
-
73
-	/**
74
-	 * @param \OCP\IConfig $config
75
-	 */
76
-	public function __construct(IConfig $config) {
77
-		$this->config = $config;
78
-		$cachedUsers = &$this->cachedUsers;
79
-		$this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
80
-			/** @var \OC\User\User $user */
81
-			unset($cachedUsers[$user->getUID()]);
82
-		});
83
-	}
84
-
85
-	/**
86
-	 * Get the active backends
87
-	 * @return \OCP\UserInterface[]
88
-	 */
89
-	public function getBackends() {
90
-		return $this->backends;
91
-	}
92
-
93
-	/**
94
-	 * register a user backend
95
-	 *
96
-	 * @param \OCP\UserInterface $backend
97
-	 */
98
-	public function registerBackend($backend) {
99
-		$this->backends[] = $backend;
100
-	}
101
-
102
-	/**
103
-	 * remove a user backend
104
-	 *
105
-	 * @param \OCP\UserInterface $backend
106
-	 */
107
-	public function removeBackend($backend) {
108
-		$this->cachedUsers = array();
109
-		if (($i = array_search($backend, $this->backends)) !== false) {
110
-			unset($this->backends[$i]);
111
-		}
112
-	}
113
-
114
-	/**
115
-	 * remove all user backends
116
-	 */
117
-	public function clearBackends() {
118
-		$this->cachedUsers = array();
119
-		$this->backends = array();
120
-	}
121
-
122
-	/**
123
-	 * get a user by user id
124
-	 *
125
-	 * @param string $uid
126
-	 * @return \OC\User\User|null Either the user or null if the specified user does not exist
127
-	 */
128
-	public function get($uid) {
129
-		if (is_null($uid) || $uid === '' || $uid === false) {
130
-			return null;
131
-		}
132
-		if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
133
-			return $this->cachedUsers[$uid];
134
-		}
135
-		foreach ($this->backends as $backend) {
136
-			if ($backend->userExists($uid)) {
137
-				return $this->getUserObject($uid, $backend);
138
-			}
139
-		}
140
-		return null;
141
-	}
142
-
143
-	/**
144
-	 * get or construct the user object
145
-	 *
146
-	 * @param string $uid
147
-	 * @param \OCP\UserInterface $backend
148
-	 * @param bool $cacheUser If false the newly created user object will not be cached
149
-	 * @return \OC\User\User
150
-	 */
151
-	protected function getUserObject($uid, $backend, $cacheUser = true) {
152
-		if (isset($this->cachedUsers[$uid])) {
153
-			return $this->cachedUsers[$uid];
154
-		}
155
-
156
-		if (method_exists($backend, 'loginName2UserName')) {
157
-			$loginName = $backend->loginName2UserName($uid);
158
-			if ($loginName !== false) {
159
-				$uid = $loginName;
160
-			}
161
-			if (isset($this->cachedUsers[$uid])) {
162
-				return $this->cachedUsers[$uid];
163
-			}
164
-		}
165
-
166
-		$user = new User($uid, $backend, $this, $this->config);
167
-		if ($cacheUser) {
168
-			$this->cachedUsers[$uid] = $user;
169
-		}
170
-		return $user;
171
-	}
172
-
173
-	/**
174
-	 * check if a user exists
175
-	 *
176
-	 * @param string $uid
177
-	 * @return bool
178
-	 */
179
-	public function userExists($uid) {
180
-		$user = $this->get($uid);
181
-		return ($user !== null);
182
-	}
183
-
184
-	/**
185
-	 * Check if the password is valid for the user
186
-	 *
187
-	 * @param string $loginName
188
-	 * @param string $password
189
-	 * @return mixed the User object on success, false otherwise
190
-	 */
191
-	public function checkPassword($loginName, $password) {
192
-		$result = $this->checkPasswordNoLogging($loginName, $password);
193
-
194
-		if ($result === false) {
195
-			\OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
196
-		}
197
-
198
-		return $result;
199
-	}
200
-
201
-	/**
202
-	 * Check if the password is valid for the user
203
-	 *
204
-	 * @internal
205
-	 * @param string $loginName
206
-	 * @param string $password
207
-	 * @return mixed the User object on success, false otherwise
208
-	 */
209
-	public function checkPasswordNoLogging($loginName, $password) {
210
-		$loginName = str_replace("\0", '', $loginName);
211
-		$password = str_replace("\0", '', $password);
212
-
213
-		foreach ($this->backends as $backend) {
214
-			if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
215
-				$uid = $backend->checkPassword($loginName, $password);
216
-				if ($uid !== false) {
217
-					return $this->getUserObject($uid, $backend);
218
-				}
219
-			}
220
-		}
221
-
222
-		return false;
223
-	}
224
-
225
-	/**
226
-	 * search by user id
227
-	 *
228
-	 * @param string $pattern
229
-	 * @param int $limit
230
-	 * @param int $offset
231
-	 * @return \OC\User\User[]
232
-	 */
233
-	public function search($pattern, $limit = null, $offset = null) {
234
-		$users = array();
235
-		foreach ($this->backends as $backend) {
236
-			$backendUsers = $backend->getUsers($pattern, $limit, $offset);
237
-			if (is_array($backendUsers)) {
238
-				foreach ($backendUsers as $uid) {
239
-					$users[$uid] = $this->getUserObject($uid, $backend);
240
-				}
241
-			}
242
-		}
243
-
244
-		uasort($users, function ($a, $b) {
245
-			/**
246
-			 * @var \OC\User\User $a
247
-			 * @var \OC\User\User $b
248
-			 */
249
-			return strcmp($a->getUID(), $b->getUID());
250
-		});
251
-		return $users;
252
-	}
253
-
254
-	/**
255
-	 * search by displayName
256
-	 *
257
-	 * @param string $pattern
258
-	 * @param int $limit
259
-	 * @param int $offset
260
-	 * @return \OC\User\User[]
261
-	 */
262
-	public function searchDisplayName($pattern, $limit = null, $offset = null) {
263
-		$users = array();
264
-		foreach ($this->backends as $backend) {
265
-			$backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
266
-			if (is_array($backendUsers)) {
267
-				foreach ($backendUsers as $uid => $displayName) {
268
-					$users[] = $this->getUserObject($uid, $backend);
269
-				}
270
-			}
271
-		}
272
-
273
-		usort($users, function ($a, $b) {
274
-			/**
275
-			 * @var \OC\User\User $a
276
-			 * @var \OC\User\User $b
277
-			 */
278
-			return strcmp(strtolower($a->getDisplayName()), strtolower($b->getDisplayName()));
279
-		});
280
-		return $users;
281
-	}
282
-
283
-	/**
284
-	 * @param string $uid
285
-	 * @param string $password
286
-	 * @throws \InvalidArgumentException
287
-	 * @return bool|IUser the created user or false
288
-	 */
289
-	public function createUser($uid, $password) {
290
-		$localBackends = [];
291
-		foreach ($this->backends as $backend) {
292
-			if ($backend instanceof Database) {
293
-				// First check if there is another user backend
294
-				$localBackends[] = $backend;
295
-				continue;
296
-			}
297
-
298
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
299
-				return $this->createUserFromBackend($uid, $password, $backend);
300
-			}
301
-		}
302
-
303
-		foreach ($localBackends as $backend) {
304
-			if ($backend->implementsActions(Backend::CREATE_USER)) {
305
-				return $this->createUserFromBackend($uid, $password, $backend);
306
-			}
307
-		}
308
-
309
-		return false;
310
-	}
311
-
312
-	/**
313
-	 * @param string $uid
314
-	 * @param string $password
315
-	 * @param UserInterface $backend
316
-	 * @return IUser|null
317
-	 * @throws \InvalidArgumentException
318
-	 */
319
-	public function createUserFromBackend($uid, $password, UserInterface $backend) {
320
-		$l = \OC::$server->getL10N('lib');
321
-
322
-		// Check the name for bad characters
323
-		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
324
-		if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) {
325
-			throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
326
-				. ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
327
-		}
328
-		// No empty username
329
-		if (trim($uid) === '') {
330
-			throw new \InvalidArgumentException($l->t('A valid username must be provided'));
331
-		}
332
-		// No whitespace at the beginning or at the end
333
-		if (trim($uid) !== $uid) {
334
-			throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
335
-		}
336
-		// Username only consists of 1 or 2 dots (directory traversal)
337
-		if ($uid === '.' || $uid === '..') {
338
-			throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
339
-		}
340
-		// No empty password
341
-		if (trim($password) === '') {
342
-			throw new \InvalidArgumentException($l->t('A valid password must be provided'));
343
-		}
344
-
345
-		// Check if user already exists
346
-		if ($this->userExists($uid)) {
347
-			throw new \InvalidArgumentException($l->t('The username is already being used'));
348
-		}
349
-
350
-		$this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
351
-		$backend->createUser($uid, $password);
352
-		$user = $this->getUserObject($uid, $backend);
353
-		if ($user instanceof IUser) {
354
-			$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
355
-		}
356
-		return $user;
357
-	}
358
-
359
-	/**
360
-	 * returns how many users per backend exist (if supported by backend)
361
-	 *
362
-	 * @param boolean $hasLoggedIn when true only users that have a lastLogin
363
-	 *                entry in the preferences table will be affected
364
-	 * @return array|int an array of backend class as key and count number as value
365
-	 *                if $hasLoggedIn is true only an int is returned
366
-	 */
367
-	public function countUsers($hasLoggedIn = false) {
368
-		if ($hasLoggedIn) {
369
-			return $this->countSeenUsers();
370
-		}
371
-		$userCountStatistics = [];
372
-		foreach ($this->backends as $backend) {
373
-			if ($backend->implementsActions(Backend::COUNT_USERS)) {
374
-				$backendUsers = $backend->countUsers();
375
-				if($backendUsers !== false) {
376
-					if($backend instanceof IUserBackend) {
377
-						$name = $backend->getBackendName();
378
-					} else {
379
-						$name = get_class($backend);
380
-					}
381
-					if(isset($userCountStatistics[$name])) {
382
-						$userCountStatistics[$name] += $backendUsers;
383
-					} else {
384
-						$userCountStatistics[$name] = $backendUsers;
385
-					}
386
-				}
387
-			}
388
-		}
389
-		return $userCountStatistics;
390
-	}
391
-
392
-	/**
393
-	 * The callback is executed for each user on each backend.
394
-	 * If the callback returns false no further users will be retrieved.
395
-	 *
396
-	 * @param \Closure $callback
397
-	 * @param string $search
398
-	 * @param boolean $onlySeen when true only users that have a lastLogin entry
399
-	 *                in the preferences table will be affected
400
-	 * @since 9.0.0
401
-	 */
402
-	public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
403
-		if ($onlySeen) {
404
-			$this->callForSeenUsers($callback);
405
-		} else {
406
-			foreach ($this->getBackends() as $backend) {
407
-				$limit = 500;
408
-				$offset = 0;
409
-				do {
410
-					$users = $backend->getUsers($search, $limit, $offset);
411
-					foreach ($users as $uid) {
412
-						if (!$backend->userExists($uid)) {
413
-							continue;
414
-						}
415
-						$user = $this->getUserObject($uid, $backend, false);
416
-						$return = $callback($user);
417
-						if ($return === false) {
418
-							break;
419
-						}
420
-					}
421
-					$offset += $limit;
422
-				} while (count($users) >= $limit);
423
-			}
424
-		}
425
-	}
426
-
427
-	/**
428
-	 * returns how many users have logged in once
429
-	 *
430
-	 * @return int
431
-	 * @since 12.0.0
432
-	 */
433
-	public function countDisabledUsers() {
434
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
435
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
436
-			->from('preferences')
437
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
438
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
439
-			->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false')));
440
-
441
-		$query = $queryBuilder->execute();
442
-
443
-		$result = (int)$query->fetchColumn();
444
-		$query->closeCursor();
445
-
446
-		return $result;
447
-	}
448
-
449
-	/**
450
-	 * returns how many users have logged in once
451
-	 *
452
-	 * @return int
453
-	 * @since 11.0.0
454
-	 */
455
-	public function countSeenUsers() {
456
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
457
-		$queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
458
-			->from('preferences')
459
-			->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
460
-			->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
461
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
462
-
463
-		$query = $queryBuilder->execute();
464
-
465
-		$result = (int)$query->fetchColumn();
466
-		$query->closeCursor();
467
-
468
-		return $result;
469
-	}
470
-
471
-	/**
472
-	 * @param \Closure $callback
473
-	 * @since 11.0.0
474
-	 */
475
-	public function callForSeenUsers(\Closure $callback) {
476
-		$limit = 1000;
477
-		$offset = 0;
478
-		do {
479
-			$userIds = $this->getSeenUserIds($limit, $offset);
480
-			$offset += $limit;
481
-			foreach ($userIds as $userId) {
482
-				foreach ($this->backends as $backend) {
483
-					if ($backend->userExists($userId)) {
484
-						$user = $this->getUserObject($userId, $backend, false);
485
-						$return = $callback($user);
486
-						if ($return === false) {
487
-							return;
488
-						}
489
-					}
490
-				}
491
-			}
492
-		} while (count($userIds) >= $limit);
493
-	}
494
-
495
-	/**
496
-	 * Getting all userIds that have a listLogin value requires checking the
497
-	 * value in php because on oracle you cannot use a clob in a where clause,
498
-	 * preventing us from doing a not null or length(value) > 0 check.
499
-	 * 
500
-	 * @param int $limit
501
-	 * @param int $offset
502
-	 * @return string[] with user ids
503
-	 */
504
-	private function getSeenUserIds($limit = null, $offset = null) {
505
-		$queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
506
-		$queryBuilder->select(['userid'])
507
-			->from('preferences')
508
-			->where($queryBuilder->expr()->eq(
509
-				'appid', $queryBuilder->createNamedParameter('login'))
510
-			)
511
-			->andWhere($queryBuilder->expr()->eq(
512
-				'configkey', $queryBuilder->createNamedParameter('lastLogin'))
513
-			)
514
-			->andWhere($queryBuilder->expr()->isNotNull('configvalue')
515
-			);
516
-
517
-		if ($limit !== null) {
518
-			$queryBuilder->setMaxResults($limit);
519
-		}
520
-		if ($offset !== null) {
521
-			$queryBuilder->setFirstResult($offset);
522
-		}
523
-		$query = $queryBuilder->execute();
524
-		$result = [];
525
-
526
-		while ($row = $query->fetch()) {
527
-			$result[] = $row['userid'];
528
-		}
529
-
530
-		$query->closeCursor();
531
-
532
-		return $result;
533
-	}
534
-
535
-	/**
536
-	 * @param string $email
537
-	 * @return IUser[]
538
-	 * @since 9.1.0
539
-	 */
540
-	public function getByEmail($email) {
541
-		$userIds = $this->config->getUsersForUserValue('settings', 'email', $email);
542
-
543
-		return array_map(function($uid) {
544
-			return $this->get($uid);
545
-		}, $userIds);
546
-	}
58
+    /**
59
+     * @var \OCP\UserInterface[] $backends
60
+     */
61
+    private $backends = array();
62
+
63
+    /**
64
+     * @var \OC\User\User[] $cachedUsers
65
+     */
66
+    private $cachedUsers = array();
67
+
68
+    /**
69
+     * @var \OCP\IConfig $config
70
+     */
71
+    private $config;
72
+
73
+    /**
74
+     * @param \OCP\IConfig $config
75
+     */
76
+    public function __construct(IConfig $config) {
77
+        $this->config = $config;
78
+        $cachedUsers = &$this->cachedUsers;
79
+        $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) {
80
+            /** @var \OC\User\User $user */
81
+            unset($cachedUsers[$user->getUID()]);
82
+        });
83
+    }
84
+
85
+    /**
86
+     * Get the active backends
87
+     * @return \OCP\UserInterface[]
88
+     */
89
+    public function getBackends() {
90
+        return $this->backends;
91
+    }
92
+
93
+    /**
94
+     * register a user backend
95
+     *
96
+     * @param \OCP\UserInterface $backend
97
+     */
98
+    public function registerBackend($backend) {
99
+        $this->backends[] = $backend;
100
+    }
101
+
102
+    /**
103
+     * remove a user backend
104
+     *
105
+     * @param \OCP\UserInterface $backend
106
+     */
107
+    public function removeBackend($backend) {
108
+        $this->cachedUsers = array();
109
+        if (($i = array_search($backend, $this->backends)) !== false) {
110
+            unset($this->backends[$i]);
111
+        }
112
+    }
113
+
114
+    /**
115
+     * remove all user backends
116
+     */
117
+    public function clearBackends() {
118
+        $this->cachedUsers = array();
119
+        $this->backends = array();
120
+    }
121
+
122
+    /**
123
+     * get a user by user id
124
+     *
125
+     * @param string $uid
126
+     * @return \OC\User\User|null Either the user or null if the specified user does not exist
127
+     */
128
+    public function get($uid) {
129
+        if (is_null($uid) || $uid === '' || $uid === false) {
130
+            return null;
131
+        }
132
+        if (isset($this->cachedUsers[$uid])) { //check the cache first to prevent having to loop over the backends
133
+            return $this->cachedUsers[$uid];
134
+        }
135
+        foreach ($this->backends as $backend) {
136
+            if ($backend->userExists($uid)) {
137
+                return $this->getUserObject($uid, $backend);
138
+            }
139
+        }
140
+        return null;
141
+    }
142
+
143
+    /**
144
+     * get or construct the user object
145
+     *
146
+     * @param string $uid
147
+     * @param \OCP\UserInterface $backend
148
+     * @param bool $cacheUser If false the newly created user object will not be cached
149
+     * @return \OC\User\User
150
+     */
151
+    protected function getUserObject($uid, $backend, $cacheUser = true) {
152
+        if (isset($this->cachedUsers[$uid])) {
153
+            return $this->cachedUsers[$uid];
154
+        }
155
+
156
+        if (method_exists($backend, 'loginName2UserName')) {
157
+            $loginName = $backend->loginName2UserName($uid);
158
+            if ($loginName !== false) {
159
+                $uid = $loginName;
160
+            }
161
+            if (isset($this->cachedUsers[$uid])) {
162
+                return $this->cachedUsers[$uid];
163
+            }
164
+        }
165
+
166
+        $user = new User($uid, $backend, $this, $this->config);
167
+        if ($cacheUser) {
168
+            $this->cachedUsers[$uid] = $user;
169
+        }
170
+        return $user;
171
+    }
172
+
173
+    /**
174
+     * check if a user exists
175
+     *
176
+     * @param string $uid
177
+     * @return bool
178
+     */
179
+    public function userExists($uid) {
180
+        $user = $this->get($uid);
181
+        return ($user !== null);
182
+    }
183
+
184
+    /**
185
+     * Check if the password is valid for the user
186
+     *
187
+     * @param string $loginName
188
+     * @param string $password
189
+     * @return mixed the User object on success, false otherwise
190
+     */
191
+    public function checkPassword($loginName, $password) {
192
+        $result = $this->checkPasswordNoLogging($loginName, $password);
193
+
194
+        if ($result === false) {
195
+            \OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']);
196
+        }
197
+
198
+        return $result;
199
+    }
200
+
201
+    /**
202
+     * Check if the password is valid for the user
203
+     *
204
+     * @internal
205
+     * @param string $loginName
206
+     * @param string $password
207
+     * @return mixed the User object on success, false otherwise
208
+     */
209
+    public function checkPasswordNoLogging($loginName, $password) {
210
+        $loginName = str_replace("\0", '', $loginName);
211
+        $password = str_replace("\0", '', $password);
212
+
213
+        foreach ($this->backends as $backend) {
214
+            if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
215
+                $uid = $backend->checkPassword($loginName, $password);
216
+                if ($uid !== false) {
217
+                    return $this->getUserObject($uid, $backend);
218
+                }
219
+            }
220
+        }
221
+
222
+        return false;
223
+    }
224
+
225
+    /**
226
+     * search by user id
227
+     *
228
+     * @param string $pattern
229
+     * @param int $limit
230
+     * @param int $offset
231
+     * @return \OC\User\User[]
232
+     */
233
+    public function search($pattern, $limit = null, $offset = null) {
234
+        $users = array();
235
+        foreach ($this->backends as $backend) {
236
+            $backendUsers = $backend->getUsers($pattern, $limit, $offset);
237
+            if (is_array($backendUsers)) {
238
+                foreach ($backendUsers as $uid) {
239
+                    $users[$uid] = $this->getUserObject($uid, $backend);
240
+                }
241
+            }
242
+        }
243
+
244
+        uasort($users, function ($a, $b) {
245
+            /**
246
+             * @var \OC\User\User $a
247
+             * @var \OC\User\User $b
248
+             */
249
+            return strcmp($a->getUID(), $b->getUID());
250
+        });
251
+        return $users;
252
+    }
253
+
254
+    /**
255
+     * search by displayName
256
+     *
257
+     * @param string $pattern
258
+     * @param int $limit
259
+     * @param int $offset
260
+     * @return \OC\User\User[]
261
+     */
262
+    public function searchDisplayName($pattern, $limit = null, $offset = null) {
263
+        $users = array();
264
+        foreach ($this->backends as $backend) {
265
+            $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset);
266
+            if (is_array($backendUsers)) {
267
+                foreach ($backendUsers as $uid => $displayName) {
268
+                    $users[] = $this->getUserObject($uid, $backend);
269
+                }
270
+            }
271
+        }
272
+
273
+        usort($users, function ($a, $b) {
274
+            /**
275
+             * @var \OC\User\User $a
276
+             * @var \OC\User\User $b
277
+             */
278
+            return strcmp(strtolower($a->getDisplayName()), strtolower($b->getDisplayName()));
279
+        });
280
+        return $users;
281
+    }
282
+
283
+    /**
284
+     * @param string $uid
285
+     * @param string $password
286
+     * @throws \InvalidArgumentException
287
+     * @return bool|IUser the created user or false
288
+     */
289
+    public function createUser($uid, $password) {
290
+        $localBackends = [];
291
+        foreach ($this->backends as $backend) {
292
+            if ($backend instanceof Database) {
293
+                // First check if there is another user backend
294
+                $localBackends[] = $backend;
295
+                continue;
296
+            }
297
+
298
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
299
+                return $this->createUserFromBackend($uid, $password, $backend);
300
+            }
301
+        }
302
+
303
+        foreach ($localBackends as $backend) {
304
+            if ($backend->implementsActions(Backend::CREATE_USER)) {
305
+                return $this->createUserFromBackend($uid, $password, $backend);
306
+            }
307
+        }
308
+
309
+        return false;
310
+    }
311
+
312
+    /**
313
+     * @param string $uid
314
+     * @param string $password
315
+     * @param UserInterface $backend
316
+     * @return IUser|null
317
+     * @throws \InvalidArgumentException
318
+     */
319
+    public function createUserFromBackend($uid, $password, UserInterface $backend) {
320
+        $l = \OC::$server->getL10N('lib');
321
+
322
+        // Check the name for bad characters
323
+        // Allowed are: "a-z", "A-Z", "0-9" and "_.@-'"
324
+        if (preg_match('/[^a-zA-Z0-9 _\.@\-\']/', $uid)) {
325
+            throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:'
326
+                . ' "a-z", "A-Z", "0-9", and "_.@-\'"'));
327
+        }
328
+        // No empty username
329
+        if (trim($uid) === '') {
330
+            throw new \InvalidArgumentException($l->t('A valid username must be provided'));
331
+        }
332
+        // No whitespace at the beginning or at the end
333
+        if (trim($uid) !== $uid) {
334
+            throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end'));
335
+        }
336
+        // Username only consists of 1 or 2 dots (directory traversal)
337
+        if ($uid === '.' || $uid === '..') {
338
+            throw new \InvalidArgumentException($l->t('Username must not consist of dots only'));
339
+        }
340
+        // No empty password
341
+        if (trim($password) === '') {
342
+            throw new \InvalidArgumentException($l->t('A valid password must be provided'));
343
+        }
344
+
345
+        // Check if user already exists
346
+        if ($this->userExists($uid)) {
347
+            throw new \InvalidArgumentException($l->t('The username is already being used'));
348
+        }
349
+
350
+        $this->emit('\OC\User', 'preCreateUser', [$uid, $password]);
351
+        $backend->createUser($uid, $password);
352
+        $user = $this->getUserObject($uid, $backend);
353
+        if ($user instanceof IUser) {
354
+            $this->emit('\OC\User', 'postCreateUser', [$user, $password]);
355
+        }
356
+        return $user;
357
+    }
358
+
359
+    /**
360
+     * returns how many users per backend exist (if supported by backend)
361
+     *
362
+     * @param boolean $hasLoggedIn when true only users that have a lastLogin
363
+     *                entry in the preferences table will be affected
364
+     * @return array|int an array of backend class as key and count number as value
365
+     *                if $hasLoggedIn is true only an int is returned
366
+     */
367
+    public function countUsers($hasLoggedIn = false) {
368
+        if ($hasLoggedIn) {
369
+            return $this->countSeenUsers();
370
+        }
371
+        $userCountStatistics = [];
372
+        foreach ($this->backends as $backend) {
373
+            if ($backend->implementsActions(Backend::COUNT_USERS)) {
374
+                $backendUsers = $backend->countUsers();
375
+                if($backendUsers !== false) {
376
+                    if($backend instanceof IUserBackend) {
377
+                        $name = $backend->getBackendName();
378
+                    } else {
379
+                        $name = get_class($backend);
380
+                    }
381
+                    if(isset($userCountStatistics[$name])) {
382
+                        $userCountStatistics[$name] += $backendUsers;
383
+                    } else {
384
+                        $userCountStatistics[$name] = $backendUsers;
385
+                    }
386
+                }
387
+            }
388
+        }
389
+        return $userCountStatistics;
390
+    }
391
+
392
+    /**
393
+     * The callback is executed for each user on each backend.
394
+     * If the callback returns false no further users will be retrieved.
395
+     *
396
+     * @param \Closure $callback
397
+     * @param string $search
398
+     * @param boolean $onlySeen when true only users that have a lastLogin entry
399
+     *                in the preferences table will be affected
400
+     * @since 9.0.0
401
+     */
402
+    public function callForAllUsers(\Closure $callback, $search = '', $onlySeen = false) {
403
+        if ($onlySeen) {
404
+            $this->callForSeenUsers($callback);
405
+        } else {
406
+            foreach ($this->getBackends() as $backend) {
407
+                $limit = 500;
408
+                $offset = 0;
409
+                do {
410
+                    $users = $backend->getUsers($search, $limit, $offset);
411
+                    foreach ($users as $uid) {
412
+                        if (!$backend->userExists($uid)) {
413
+                            continue;
414
+                        }
415
+                        $user = $this->getUserObject($uid, $backend, false);
416
+                        $return = $callback($user);
417
+                        if ($return === false) {
418
+                            break;
419
+                        }
420
+                    }
421
+                    $offset += $limit;
422
+                } while (count($users) >= $limit);
423
+            }
424
+        }
425
+    }
426
+
427
+    /**
428
+     * returns how many users have logged in once
429
+     *
430
+     * @return int
431
+     * @since 12.0.0
432
+     */
433
+    public function countDisabledUsers() {
434
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
435
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
436
+            ->from('preferences')
437
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('core')))
438
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled')))
439
+            ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false')));
440
+
441
+        $query = $queryBuilder->execute();
442
+
443
+        $result = (int)$query->fetchColumn();
444
+        $query->closeCursor();
445
+
446
+        return $result;
447
+    }
448
+
449
+    /**
450
+     * returns how many users have logged in once
451
+     *
452
+     * @return int
453
+     * @since 11.0.0
454
+     */
455
+    public function countSeenUsers() {
456
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
457
+        $queryBuilder->select($queryBuilder->createFunction('COUNT(*)'))
458
+            ->from('preferences')
459
+            ->where($queryBuilder->expr()->eq('appid', $queryBuilder->createNamedParameter('login')))
460
+            ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('lastLogin')))
461
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue'));
462
+
463
+        $query = $queryBuilder->execute();
464
+
465
+        $result = (int)$query->fetchColumn();
466
+        $query->closeCursor();
467
+
468
+        return $result;
469
+    }
470
+
471
+    /**
472
+     * @param \Closure $callback
473
+     * @since 11.0.0
474
+     */
475
+    public function callForSeenUsers(\Closure $callback) {
476
+        $limit = 1000;
477
+        $offset = 0;
478
+        do {
479
+            $userIds = $this->getSeenUserIds($limit, $offset);
480
+            $offset += $limit;
481
+            foreach ($userIds as $userId) {
482
+                foreach ($this->backends as $backend) {
483
+                    if ($backend->userExists($userId)) {
484
+                        $user = $this->getUserObject($userId, $backend, false);
485
+                        $return = $callback($user);
486
+                        if ($return === false) {
487
+                            return;
488
+                        }
489
+                    }
490
+                }
491
+            }
492
+        } while (count($userIds) >= $limit);
493
+    }
494
+
495
+    /**
496
+     * Getting all userIds that have a listLogin value requires checking the
497
+     * value in php because on oracle you cannot use a clob in a where clause,
498
+     * preventing us from doing a not null or length(value) > 0 check.
499
+     * 
500
+     * @param int $limit
501
+     * @param int $offset
502
+     * @return string[] with user ids
503
+     */
504
+    private function getSeenUserIds($limit = null, $offset = null) {
505
+        $queryBuilder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
506
+        $queryBuilder->select(['userid'])
507
+            ->from('preferences')
508
+            ->where($queryBuilder->expr()->eq(
509
+                'appid', $queryBuilder->createNamedParameter('login'))
510
+            )
511
+            ->andWhere($queryBuilder->expr()->eq(
512
+                'configkey', $queryBuilder->createNamedParameter('lastLogin'))
513
+            )
514
+            ->andWhere($queryBuilder->expr()->isNotNull('configvalue')
515
+            );
516
+
517
+        if ($limit !== null) {
518
+            $queryBuilder->setMaxResults($limit);
519
+        }
520
+        if ($offset !== null) {
521
+            $queryBuilder->setFirstResult($offset);
522
+        }
523
+        $query = $queryBuilder->execute();
524
+        $result = [];
525
+
526
+        while ($row = $query->fetch()) {
527
+            $result[] = $row['userid'];
528
+        }
529
+
530
+        $query->closeCursor();
531
+
532
+        return $result;
533
+    }
534
+
535
+    /**
536
+     * @param string $email
537
+     * @return IUser[]
538
+     * @since 9.1.0
539
+     */
540
+    public function getByEmail($email) {
541
+        $userIds = $this->config->getUsersForUserValue('settings', 'email', $email);
542
+
543
+        return array_map(function($uid) {
544
+            return $this->get($uid);
545
+        }, $userIds);
546
+    }
547 547
 }
Please login to merge, or discard this patch.