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