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