Passed
Push — master ( 62ff4f...9124c6 )
by John
74:39 queued 12s
created
lib/private/Accounts/Account.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -36,97 +36,97 @@
 block discarded – undo
36 36
 use RuntimeException;
37 37
 
38 38
 class Account implements IAccount {
39
-	use TAccountsHelper;
40
-
41
-	/** @var IAccountPropertyCollection[]|IAccountProperty[] */
42
-	private $properties = [];
43
-
44
-	/** @var IUser */
45
-	private $user;
46
-
47
-	public function __construct(IUser $user) {
48
-		$this->user = $user;
49
-	}
50
-
51
-	public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount {
52
-		if ($this->isCollection($property)) {
53
-			throw new \InvalidArgumentException('setProperty cannot set an IAccountsPropertyCollection');
54
-		}
55
-		$this->properties[$property] = new AccountProperty($property, $value, $scope, $verified, $verificationData);
56
-		return $this;
57
-	}
58
-
59
-	public function getProperty(string $property): IAccountProperty {
60
-		if ($this->isCollection($property)) {
61
-			throw new \InvalidArgumentException('getProperty cannot retrieve an IAccountsPropertyCollection');
62
-		}
63
-		if (!array_key_exists($property, $this->properties) || !$this->properties[$property] instanceof IAccountProperty) {
64
-			throw new PropertyDoesNotExistException($property);
65
-		}
66
-		return $this->properties[$property];
67
-	}
68
-
69
-	public function getProperties(): array {
70
-		return array_filter($this->properties, function ($obj) {
71
-			return $obj instanceof IAccountProperty;
72
-		});
73
-	}
74
-
75
-	public function getAllProperties(): Generator {
76
-		foreach ($this->properties as $propertyObject) {
77
-			if ($propertyObject instanceof IAccountProperty) {
78
-				yield $propertyObject;
79
-			} elseif ($propertyObject instanceof IAccountPropertyCollection) {
80
-				foreach ($propertyObject->getProperties() as $property) {
81
-					yield $property;
82
-				}
83
-			}
84
-		}
85
-	}
86
-
87
-	public function getFilteredProperties(string $scope = null, string $verified = null): array {
88
-		$result = $incrementals = [];
89
-		/** @var IAccountProperty $obj */
90
-		foreach ($this->getAllProperties() as $obj) {
91
-			if ($scope !== null && $scope !== $obj->getScope()) {
92
-				continue;
93
-			}
94
-			if ($verified !== null && $verified !== $obj->getVerified()) {
95
-				continue;
96
-			}
97
-			$index = $obj->getName();
98
-			if ($this->isCollection($index)) {
99
-				$incrementals[$index] = ($incrementals[$index] ?? -1) + 1;
100
-				$index .= '#' . $incrementals[$index];
101
-			}
102
-			$result[$index] = $obj;
103
-		}
104
-		return $result;
105
-	}
106
-
107
-	public function jsonSerialize() {
108
-		return $this->properties;
109
-	}
110
-
111
-	public function getUser(): IUser {
112
-		return $this->user;
113
-	}
114
-
115
-	public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount {
116
-		$this->properties[$propertyCollection->getName()] = $propertyCollection;
117
-		return $this;
118
-	}
119
-
120
-	public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection {
121
-		if (!$this->isCollection($propertyCollectionName)) {
122
-			throw new PropertyDoesNotExistException($propertyCollectionName);
123
-		}
124
-		if (!array_key_exists($propertyCollectionName, $this->properties)) {
125
-			$this->properties[$propertyCollectionName] = new AccountPropertyCollection($propertyCollectionName);
126
-		}
127
-		if (!$this->properties[$propertyCollectionName] instanceof IAccountPropertyCollection) {
128
-			throw new RuntimeException('Requested collection is not an IAccountPropertyCollection');
129
-		}
130
-		return $this->properties[$propertyCollectionName];
131
-	}
39
+    use TAccountsHelper;
40
+
41
+    /** @var IAccountPropertyCollection[]|IAccountProperty[] */
42
+    private $properties = [];
43
+
44
+    /** @var IUser */
45
+    private $user;
46
+
47
+    public function __construct(IUser $user) {
48
+        $this->user = $user;
49
+    }
50
+
51
+    public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount {
52
+        if ($this->isCollection($property)) {
53
+            throw new \InvalidArgumentException('setProperty cannot set an IAccountsPropertyCollection');
54
+        }
55
+        $this->properties[$property] = new AccountProperty($property, $value, $scope, $verified, $verificationData);
56
+        return $this;
57
+    }
58
+
59
+    public function getProperty(string $property): IAccountProperty {
60
+        if ($this->isCollection($property)) {
61
+            throw new \InvalidArgumentException('getProperty cannot retrieve an IAccountsPropertyCollection');
62
+        }
63
+        if (!array_key_exists($property, $this->properties) || !$this->properties[$property] instanceof IAccountProperty) {
64
+            throw new PropertyDoesNotExistException($property);
65
+        }
66
+        return $this->properties[$property];
67
+    }
68
+
69
+    public function getProperties(): array {
70
+        return array_filter($this->properties, function ($obj) {
71
+            return $obj instanceof IAccountProperty;
72
+        });
73
+    }
74
+
75
+    public function getAllProperties(): Generator {
76
+        foreach ($this->properties as $propertyObject) {
77
+            if ($propertyObject instanceof IAccountProperty) {
78
+                yield $propertyObject;
79
+            } elseif ($propertyObject instanceof IAccountPropertyCollection) {
80
+                foreach ($propertyObject->getProperties() as $property) {
81
+                    yield $property;
82
+                }
83
+            }
84
+        }
85
+    }
86
+
87
+    public function getFilteredProperties(string $scope = null, string $verified = null): array {
88
+        $result = $incrementals = [];
89
+        /** @var IAccountProperty $obj */
90
+        foreach ($this->getAllProperties() as $obj) {
91
+            if ($scope !== null && $scope !== $obj->getScope()) {
92
+                continue;
93
+            }
94
+            if ($verified !== null && $verified !== $obj->getVerified()) {
95
+                continue;
96
+            }
97
+            $index = $obj->getName();
98
+            if ($this->isCollection($index)) {
99
+                $incrementals[$index] = ($incrementals[$index] ?? -1) + 1;
100
+                $index .= '#' . $incrementals[$index];
101
+            }
102
+            $result[$index] = $obj;
103
+        }
104
+        return $result;
105
+    }
106
+
107
+    public function jsonSerialize() {
108
+        return $this->properties;
109
+    }
110
+
111
+    public function getUser(): IUser {
112
+        return $this->user;
113
+    }
114
+
115
+    public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount {
116
+        $this->properties[$propertyCollection->getName()] = $propertyCollection;
117
+        return $this;
118
+    }
119
+
120
+    public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection {
121
+        if (!$this->isCollection($propertyCollectionName)) {
122
+            throw new PropertyDoesNotExistException($propertyCollectionName);
123
+        }
124
+        if (!array_key_exists($propertyCollectionName, $this->properties)) {
125
+            $this->properties[$propertyCollectionName] = new AccountPropertyCollection($propertyCollectionName);
126
+        }
127
+        if (!$this->properties[$propertyCollectionName] instanceof IAccountPropertyCollection) {
128
+            throw new RuntimeException('Requested collection is not an IAccountPropertyCollection');
129
+        }
130
+        return $this->properties[$propertyCollectionName];
131
+    }
132 132
 }
Please login to merge, or discard this patch.
lib/private/Accounts/AccountPropertyCollection.php 1 patch
Indentation   +67 added lines, -67 removed lines patch added patch discarded remove patch
@@ -33,71 +33,71 @@
 block discarded – undo
33 33
 
34 34
 class AccountPropertyCollection implements IAccountPropertyCollection {
35 35
 
36
-	/** @var string */
37
-	protected $collectionName = '';
38
-
39
-	/** @var IAccountProperty[] */
40
-	protected $properties = [];
41
-
42
-	public function __construct(string $collectionName) {
43
-		$this->collectionName = $collectionName;
44
-	}
45
-
46
-	public function setProperties(array $properties): IAccountPropertyCollection {
47
-		/** @var IAccountProperty $property */
48
-		$this->properties = [];
49
-		foreach ($properties as $property) {
50
-			$this->addProperty($property);
51
-		}
52
-		return $this;
53
-	}
54
-
55
-	public function getProperties(): array {
56
-		return $this->properties;
57
-	}
58
-
59
-	public function addProperty(IAccountProperty $property): IAccountPropertyCollection {
60
-		if ($property->getName() !== $this->collectionName) {
61
-			throw new InvalidArgumentException('Provided property does not match collection name');
62
-		}
63
-		$this->properties[] = $property;
64
-		return $this;
65
-	}
66
-
67
-	public function addPropertyWithDefaults(string $value): IAccountPropertyCollection {
68
-		$property = new AccountProperty(
69
-			$this->collectionName,
70
-			$value,
71
-			IAccountManager::SCOPE_LOCAL,
72
-			IAccountManager::NOT_VERIFIED,
73
-			''
74
-		);
75
-		$this->addProperty($property);
76
-		return $this;
77
-	}
78
-
79
-	public function removeProperty(IAccountProperty $property): IAccountPropertyCollection {
80
-		$ref = array_search($property, $this->properties, true);
81
-		if ($ref !== false) {
82
-			unset($this->properties[$ref]);
83
-		}
84
-		return $this;
85
-	}
86
-
87
-	public function removePropertyByValue(string $value): IAccountPropertyCollection {
88
-		foreach ($this->properties as $i => $property) {
89
-			if ($property->getValue() === $value) {
90
-				unset($this->properties[$i]);
91
-			}
92
-		}
93
-		return $this;
94
-	}
95
-
96
-	public function jsonSerialize() {
97
-		return [$this->collectionName => $this->properties];
98
-	}
99
-
100
-	public function getName(): string {
101
-		return $this->collectionName;
102
-	}
36
+    /** @var string */
37
+    protected $collectionName = '';
38
+
39
+    /** @var IAccountProperty[] */
40
+    protected $properties = [];
41
+
42
+    public function __construct(string $collectionName) {
43
+        $this->collectionName = $collectionName;
44
+    }
45
+
46
+    public function setProperties(array $properties): IAccountPropertyCollection {
47
+        /** @var IAccountProperty $property */
48
+        $this->properties = [];
49
+        foreach ($properties as $property) {
50
+            $this->addProperty($property);
51
+        }
52
+        return $this;
53
+    }
54
+
55
+    public function getProperties(): array {
56
+        return $this->properties;
57
+    }
58
+
59
+    public function addProperty(IAccountProperty $property): IAccountPropertyCollection {
60
+        if ($property->getName() !== $this->collectionName) {
61
+            throw new InvalidArgumentException('Provided property does not match collection name');
62
+        }
63
+        $this->properties[] = $property;
64
+        return $this;
65
+    }
66
+
67
+    public function addPropertyWithDefaults(string $value): IAccountPropertyCollection {
68
+        $property = new AccountProperty(
69
+            $this->collectionName,
70
+            $value,
71
+            IAccountManager::SCOPE_LOCAL,
72
+            IAccountManager::NOT_VERIFIED,
73
+            ''
74
+        );
75
+        $this->addProperty($property);
76
+        return $this;
77
+    }
78
+
79
+    public function removeProperty(IAccountProperty $property): IAccountPropertyCollection {
80
+        $ref = array_search($property, $this->properties, true);
81
+        if ($ref !== false) {
82
+            unset($this->properties[$ref]);
83
+        }
84
+        return $this;
85
+    }
86
+
87
+    public function removePropertyByValue(string $value): IAccountPropertyCollection {
88
+        foreach ($this->properties as $i => $property) {
89
+            if ($property->getValue() === $value) {
90
+                unset($this->properties[$i]);
91
+            }
92
+        }
93
+        return $this;
94
+    }
95
+
96
+    public function jsonSerialize() {
97
+        return [$this->collectionName => $this->properties];
98
+    }
99
+
100
+    public function getName(): string {
101
+        return $this->collectionName;
102
+    }
103 103
 }
Please login to merge, or discard this patch.
lib/private/Accounts/AccountManager.php 1 patch
Indentation   +587 added lines, -587 removed lines patch added patch discarded remove patch
@@ -66,591 +66,591 @@
 block discarded – undo
66 66
  * @package OC\Accounts
67 67
  */
68 68
 class AccountManager implements IAccountManager {
69
-	use TAccountsHelper;
70
-
71
-	/** @var  IDBConnection database connection */
72
-	private $connection;
73
-
74
-	/** @var IConfig */
75
-	private $config;
76
-
77
-	/** @var string table name */
78
-	private $table = 'accounts';
79
-
80
-	/** @var string table name */
81
-	private $dataTable = 'accounts_data';
82
-
83
-	/** @var EventDispatcherInterface */
84
-	private $eventDispatcher;
85
-
86
-	/** @var IJobList */
87
-	private $jobList;
88
-
89
-	/** @var LoggerInterface */
90
-	private $logger;
91
-
92
-	public function __construct(IDBConnection $connection,
93
-								IConfig $config,
94
-								EventDispatcherInterface $eventDispatcher,
95
-								IJobList $jobList,
96
-								LoggerInterface $logger) {
97
-		$this->connection = $connection;
98
-		$this->config = $config;
99
-		$this->eventDispatcher = $eventDispatcher;
100
-		$this->jobList = $jobList;
101
-		$this->logger = $logger;
102
-	}
103
-
104
-	/**
105
-	 * @param string $input
106
-	 * @return string Provided phone number in E.164 format when it was a valid number
107
-	 * @throws InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
108
-	 */
109
-	protected function parsePhoneNumber(string $input): string {
110
-		$defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
111
-
112
-		if ($defaultRegion === '') {
113
-			// When no default region is set, only +49… numbers are valid
114
-			if (strpos($input, '+') !== 0) {
115
-				throw new InvalidArgumentException(self::PROPERTY_PHONE);
116
-			}
117
-
118
-			$defaultRegion = 'EN';
119
-		}
120
-
121
-		$phoneUtil = PhoneNumberUtil::getInstance();
122
-		try {
123
-			$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
124
-			if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
125
-				return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
126
-			}
127
-		} catch (NumberParseException $e) {
128
-		}
129
-
130
-		throw new InvalidArgumentException(self::PROPERTY_PHONE);
131
-	}
132
-
133
-	/**
134
-	 *
135
-	 * @param string $input
136
-	 * @return string
137
-	 * @throws InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty
138
-	 */
139
-	protected function parseWebsite(string $input): string {
140
-		$parts = parse_url($input);
141
-		if (!isset($parts['scheme']) || ($parts['scheme'] !== 'https' && $parts['scheme'] !== 'http')) {
142
-			throw new InvalidArgumentException(self::PROPERTY_WEBSITE);
143
-		}
144
-
145
-		if (!isset($parts['host']) || $parts['host'] === '') {
146
-			throw new InvalidArgumentException(self::PROPERTY_WEBSITE);
147
-		}
148
-
149
-		return $input;
150
-	}
151
-
152
-	/**
153
-	 * @param IAccountProperty[] $properties
154
-	 */
155
-	protected function testValueLengths(array $properties, bool $throwOnData = false): void {
156
-		foreach ($properties as $property) {
157
-			if (strlen($property->getValue()) > 2048) {
158
-				if ($throwOnData) {
159
-					throw new InvalidArgumentException();
160
-				} else {
161
-					$property->setValue('');
162
-				}
163
-			}
164
-		}
165
-	}
166
-
167
-	protected function testPropertyScope(IAccountProperty $property, array $allowedScopes, bool $throwOnData): void {
168
-		if ($throwOnData && !in_array($property->getScope(), $allowedScopes, true)) {
169
-			throw new InvalidArgumentException('scope');
170
-		}
171
-
172
-		if (
173
-			$property->getScope() === self::SCOPE_PRIVATE
174
-			&& in_array($property->getName(), [self::PROPERTY_DISPLAYNAME, self::PROPERTY_EMAIL])
175
-		) {
176
-			if ($throwOnData) {
177
-				// v2-private is not available for these fields
178
-				throw new InvalidArgumentException('scope');
179
-			} else {
180
-				// default to local
181
-				$property->setScope(self::SCOPE_LOCAL);
182
-			}
183
-		} else {
184
-			// migrate scope values to the new format
185
-			// invalid scopes are mapped to a default value
186
-			$property->setScope(AccountProperty::mapScopeToV2($property->getScope()));
187
-		}
188
-	}
189
-
190
-	protected function sanitizePhoneNumberValue(IAccountProperty $property, bool $throwOnData = false) {
191
-		if ($property->getName() !== self::PROPERTY_PHONE) {
192
-			if ($throwOnData) {
193
-				throw new InvalidArgumentException(sprintf('sanitizePhoneNumberValue can only sanitize phone numbers, %s given', $property->getName()));
194
-			}
195
-			return;
196
-		}
197
-		if ($property->getValue() === '') {
198
-			return;
199
-		}
200
-		try {
201
-			$property->setValue($this->parsePhoneNumber($property->getValue()));
202
-		} catch (InvalidArgumentException $e) {
203
-			if ($throwOnData) {
204
-				throw $e;
205
-			}
206
-			$property->setValue('');
207
-		}
208
-	}
209
-
210
-	protected function sanitizeWebsite(IAccountProperty $property, bool $throwOnData = false) {
211
-		if ($property->getName() !== self::PROPERTY_WEBSITE) {
212
-			if ($throwOnData) {
213
-				throw new InvalidArgumentException(sprintf('sanitizeWebsite can only sanitize web domains, %s given', $property->getName()));
214
-			}
215
-		}
216
-		try {
217
-			$property->setValue($this->parseWebsite($property->getValue()));
218
-		} catch (InvalidArgumentException $e) {
219
-			if ($throwOnData) {
220
-				throw $e;
221
-			}
222
-			$property->setValue('');
223
-		}
224
-	}
225
-
226
-	protected function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
227
-		$oldUserData = $this->getUser($user, false);
228
-		$updated = true;
229
-
230
-		if ($oldUserData !== $data) {
231
-			$this->updateExistingUser($user, $data);
232
-		} else {
233
-			// nothing needs to be done if new and old data set are the same
234
-			$updated = false;
235
-		}
236
-
237
-		if ($updated) {
238
-			$this->eventDispatcher->dispatch(
239
-				'OC\AccountManager::userUpdated',
240
-				new GenericEvent($user, $data)
241
-			);
242
-		}
243
-
244
-		return $data;
245
-	}
246
-
247
-	/**
248
-	 * delete user from accounts table
249
-	 *
250
-	 * @param IUser $user
251
-	 */
252
-	public function deleteUser(IUser $user) {
253
-		$uid = $user->getUID();
254
-		$query = $this->connection->getQueryBuilder();
255
-		$query->delete($this->table)
256
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
257
-			->execute();
258
-
259
-		$this->deleteUserData($user);
260
-	}
261
-
262
-	/**
263
-	 * delete user from accounts table
264
-	 *
265
-	 * @param IUser $user
266
-	 */
267
-	public function deleteUserData(IUser $user): void {
268
-		$uid = $user->getUID();
269
-		$query = $this->connection->getQueryBuilder();
270
-		$query->delete($this->dataTable)
271
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
272
-			->execute();
273
-	}
274
-
275
-	/**
276
-	 * get stored data from a given user
277
-	 */
278
-	protected function getUser(IUser $user, bool $insertIfNotExists = true): array {
279
-		$uid = $user->getUID();
280
-		$query = $this->connection->getQueryBuilder();
281
-		$query->select('data')
282
-			->from($this->table)
283
-			->where($query->expr()->eq('uid', $query->createParameter('uid')))
284
-			->setParameter('uid', $uid);
285
-		$result = $query->executeQuery();
286
-		$accountData = $result->fetchAll();
287
-		$result->closeCursor();
288
-
289
-		if (empty($accountData)) {
290
-			$userData = $this->buildDefaultUserRecord($user);
291
-			if ($insertIfNotExists) {
292
-				$this->insertNewUser($user, $userData);
293
-			}
294
-			return $userData;
295
-		}
296
-
297
-		$userDataArray = $this->importFromJson($accountData[0]['data'], $uid);
298
-		if ($userDataArray === null || $userDataArray === []) {
299
-			return $this->buildDefaultUserRecord($user);
300
-		}
301
-
302
-		return $this->addMissingDefaultValues($userDataArray);
303
-	}
304
-
305
-	public function searchUsers(string $property, array $values): array {
306
-		$chunks = array_chunk($values, 500);
307
-		$query = $this->connection->getQueryBuilder();
308
-		$query->select('*')
309
-			->from($this->dataTable)
310
-			->where($query->expr()->eq('name', $query->createNamedParameter($property)))
311
-			->andWhere($query->expr()->in('value', $query->createParameter('values')));
312
-
313
-		$matches = [];
314
-		foreach ($chunks as $chunk) {
315
-			$query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
316
-			$result = $query->executeQuery();
317
-
318
-			while ($row = $result->fetch()) {
319
-				$matches[$row['uid']] = $row['value'];
320
-			}
321
-			$result->closeCursor();
322
-		}
323
-
324
-		$result = array_merge($matches, $this->searchUsersForRelatedCollection($property, $values));
325
-
326
-		return array_flip($result);
327
-	}
328
-
329
-	protected function searchUsersForRelatedCollection(string $property, array $values): array {
330
-		switch ($property) {
331
-			case IAccountManager::PROPERTY_EMAIL:
332
-				return array_flip($this->searchUsers(IAccountManager::COLLECTION_EMAIL, $values));
333
-			default:
334
-				return [];
335
-		}
336
-	}
337
-
338
-	/**
339
-	 * check if we need to ask the server for email verification, if yes we create a cronjob
340
-	 *
341
-	 */
342
-	protected function checkEmailVerification(IAccount $updatedAccount, array $oldData): void {
343
-		try {
344
-			$property = $updatedAccount->getProperty(self::PROPERTY_EMAIL);
345
-		} catch (PropertyDoesNotExistException $e) {
346
-			return;
347
-		}
348
-		$oldMail = isset($oldData[self::PROPERTY_EMAIL]) ? $oldData[self::PROPERTY_EMAIL]['value']['value'] : '';
349
-		if ($oldMail !== $property->getValue()) {
350
-			$this->jobList->add(VerifyUserData::class,
351
-				[
352
-					'verificationCode' => '',
353
-					'data' => $property->getValue(),
354
-					'type' => self::PROPERTY_EMAIL,
355
-					'uid' => $updatedAccount->getUser()->getUID(),
356
-					'try' => 0,
357
-					'lastRun' => time()
358
-				]
359
-			);
360
-
361
-
362
-
363
-
364
-			$property->setVerified(self::VERIFICATION_IN_PROGRESS);
365
-		}
366
-	}
367
-
368
-	/**
369
-	 * make sure that all expected data are set
370
-	 *
371
-	 */
372
-	protected function addMissingDefaultValues(array $userData): array {
373
-		foreach ($userData as $i => $value) {
374
-			if (!isset($value['verified'])) {
375
-				$userData[$i]['verified'] = self::NOT_VERIFIED;
376
-			}
377
-		}
378
-
379
-		return $userData;
380
-	}
381
-
382
-	protected function updateVerificationStatus(IAccount $updatedAccount, array $oldData): void {
383
-		static $propertiesVerifiableByLookupServer = [
384
-			self::PROPERTY_TWITTER,
385
-			self::PROPERTY_WEBSITE,
386
-			self::PROPERTY_EMAIL,
387
-		];
388
-
389
-		foreach ($propertiesVerifiableByLookupServer as $propertyName) {
390
-			try {
391
-				$property = $updatedAccount->getProperty($propertyName);
392
-			} catch (PropertyDoesNotExistException $e) {
393
-				continue;
394
-			}
395
-			$wasVerified = isset($oldData[$propertyName])
396
-				&& isset($oldData[$propertyName]['verified'])
397
-				&& $oldData[$propertyName]['verified'] === self::VERIFIED;
398
-			if ((!isset($oldData[$propertyName])
399
-					|| !isset($oldData[$propertyName]['value'])
400
-					|| $property->getValue() !== $oldData[$propertyName]['value'])
401
-				&& ($property->getVerified() !== self::NOT_VERIFIED
402
-					|| $wasVerified)
403
-				) {
404
-				$property->setVerified(self::NOT_VERIFIED);
405
-			}
406
-		}
407
-	}
408
-
409
-
410
-	/**
411
-	 * add new user to accounts table
412
-	 *
413
-	 * @param IUser $user
414
-	 * @param array $data
415
-	 */
416
-	protected function insertNewUser(IUser $user, array $data): void {
417
-		$uid = $user->getUID();
418
-		$jsonEncodedData = $this->prepareJson($data);
419
-		$query = $this->connection->getQueryBuilder();
420
-		$query->insert($this->table)
421
-			->values(
422
-				[
423
-					'uid' => $query->createNamedParameter($uid),
424
-					'data' => $query->createNamedParameter($jsonEncodedData),
425
-				]
426
-			)
427
-			->executeStatement();
428
-
429
-		$this->deleteUserData($user);
430
-		$this->writeUserData($user, $data);
431
-	}
432
-
433
-	protected function prepareJson(array $data): string {
434
-		$preparedData = [];
435
-		foreach ($data as $dataRow) {
436
-			$propertyName = $dataRow['name'];
437
-			unset($dataRow['name']);
438
-			if (!$this->isCollection($propertyName)) {
439
-				$preparedData[$propertyName] = $dataRow;
440
-				continue;
441
-			}
442
-			if (!isset($preparedData[$propertyName])) {
443
-				$preparedData[$propertyName] = [];
444
-			}
445
-			$preparedData[$propertyName][] = $dataRow;
446
-		}
447
-		return json_encode($preparedData);
448
-	}
449
-
450
-	protected function importFromJson(string $json, string $userId): ?array {
451
-		$result = [];
452
-		$jsonArray = json_decode($json, true);
453
-		$jsonError = json_last_error();
454
-		if ($jsonError !== JSON_ERROR_NONE) {
455
-			$this->logger->critical(
456
-				'User data of {uid} contained invalid JSON (error {json_error}), hence falling back to a default user record',
457
-				[
458
-					'uid' => $userId,
459
-					'json_error' => $jsonError
460
-				]
461
-			);
462
-			return null;
463
-		}
464
-		foreach ($jsonArray as $propertyName => $row) {
465
-			if (!$this->isCollection($propertyName)) {
466
-				$result[] = array_merge($row, ['name' => $propertyName]);
467
-				continue;
468
-			}
469
-			foreach ($row as $singleRow) {
470
-				$result[] = array_merge($singleRow, ['name' => $propertyName]);
471
-			}
472
-		}
473
-		return $result;
474
-	}
475
-
476
-	/**
477
-	 * update existing user in accounts table
478
-	 *
479
-	 * @param IUser $user
480
-	 * @param array $data
481
-	 */
482
-	protected function updateExistingUser(IUser $user, array $data): void {
483
-		$uid = $user->getUID();
484
-		$jsonEncodedData = $this->prepareJson($data);
485
-		$query = $this->connection->getQueryBuilder();
486
-		$query->update($this->table)
487
-			->set('data', $query->createNamedParameter($jsonEncodedData))
488
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
489
-			->executeStatement();
490
-
491
-		$this->deleteUserData($user);
492
-		$this->writeUserData($user, $data);
493
-	}
494
-
495
-	protected function writeUserData(IUser $user, array $data): void {
496
-		$query = $this->connection->getQueryBuilder();
497
-		$query->insert($this->dataTable)
498
-			->values(
499
-				[
500
-					'uid' => $query->createNamedParameter($user->getUID()),
501
-					'name' => $query->createParameter('name'),
502
-					'value' => $query->createParameter('value'),
503
-				]
504
-			);
505
-		$this->writeUserDataProperties($query, $data);
506
-	}
507
-
508
-	protected function writeUserDataProperties(IQueryBuilder $query, array $data): void {
509
-		foreach ($data as $property) {
510
-			if ($property['name'] === self::PROPERTY_AVATAR) {
511
-				continue;
512
-			}
513
-
514
-
515
-			$query->setParameter('name', $property['name'])
516
-				->setParameter('value', $property['value'] ?? '');
517
-			$query->executeStatement();
518
-		}
519
-	}
520
-
521
-	/**
522
-	 * build default user record in case not data set exists yet
523
-	 *
524
-	 * @param IUser $user
525
-	 * @return array
526
-	 */
527
-	protected function buildDefaultUserRecord(IUser $user) {
528
-		return [
529
-
530
-			[
531
-				'name' => self::PROPERTY_DISPLAYNAME,
532
-				'value' => $user->getDisplayName(),
533
-				'scope' => self::SCOPE_FEDERATED,
534
-				'verified' => self::NOT_VERIFIED,
535
-			],
536
-
537
-			[
538
-				'name' => self::PROPERTY_ADDRESS,
539
-				'value' => '',
540
-				'scope' => self::SCOPE_LOCAL,
541
-				'verified' => self::NOT_VERIFIED,
542
-			],
543
-
544
-			[
545
-				'name' => self::PROPERTY_WEBSITE,
546
-				'value' => '',
547
-				'scope' => self::SCOPE_LOCAL,
548
-				'verified' => self::NOT_VERIFIED,
549
-			],
550
-
551
-			[
552
-				'name' => self::PROPERTY_EMAIL,
553
-				'value' => $user->getEMailAddress(),
554
-				'scope' => self::SCOPE_FEDERATED,
555
-				'verified' => self::NOT_VERIFIED,
556
-			],
557
-
558
-			[
559
-				'name' => self::PROPERTY_AVATAR,
560
-				'scope' => self::SCOPE_FEDERATED
561
-			],
562
-
563
-			[
564
-				'name' => self::PROPERTY_PHONE,
565
-				'value' => '',
566
-				'scope' => self::SCOPE_LOCAL,
567
-				'verified' => self::NOT_VERIFIED,
568
-			],
569
-
570
-			[
571
-				'name' => self::PROPERTY_TWITTER,
572
-				'value' => '',
573
-				'scope' => self::SCOPE_LOCAL,
574
-				'verified' => self::NOT_VERIFIED,
575
-			],
576
-
577
-		];
578
-	}
579
-
580
-	private function arrayDataToCollection(IAccount $account, array $data): IAccountPropertyCollection {
581
-		$collection = $account->getPropertyCollection($data['name']);
582
-
583
-		$p = new AccountProperty(
584
-			$data['name'],
585
-			$data['value'] ?? '',
586
-			$data['scope'] ?? self::SCOPE_LOCAL,
587
-			$data['verified'] ?? self::NOT_VERIFIED,
588
-			''
589
-		);
590
-		$collection->addProperty($p);
591
-
592
-		return $collection;
593
-	}
594
-
595
-	private function parseAccountData(IUser $user, $data): Account {
596
-		$account = new Account($user);
597
-		foreach ($data as $accountData) {
598
-			if ($this->isCollection($accountData['name'])) {
599
-				$account->setPropertyCollection($this->arrayDataToCollection($account, $accountData));
600
-			} else {
601
-				$account->setProperty($accountData['name'], $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
602
-			}
603
-		}
604
-		return $account;
605
-	}
606
-
607
-	public function getAccount(IUser $user): IAccount {
608
-		return $this->parseAccountData($user, $this->getUser($user));
609
-	}
610
-
611
-	public function updateAccount(IAccount $account): void {
612
-		$this->testValueLengths(iterator_to_array($account->getAllProperties()), true);
613
-		try {
614
-			$property = $account->getProperty(self::PROPERTY_PHONE);
615
-			$this->sanitizePhoneNumberValue($property);
616
-		} catch (PropertyDoesNotExistException $e) {
617
-			//  valid case, nothing to do
618
-		}
619
-
620
-		try {
621
-			$property = $account->getProperty(self::PROPERTY_WEBSITE);
622
-			$this->sanitizeWebsite($property);
623
-		} catch (PropertyDoesNotExistException $e) {
624
-			//  valid case, nothing to do
625
-		}
626
-
627
-		static $allowedScopes = [
628
-			self::SCOPE_PRIVATE,
629
-			self::SCOPE_LOCAL,
630
-			self::SCOPE_FEDERATED,
631
-			self::SCOPE_PUBLISHED,
632
-			self::VISIBILITY_PRIVATE,
633
-			self::VISIBILITY_CONTACTS_ONLY,
634
-			self::VISIBILITY_PUBLIC,
635
-		];
636
-		foreach ($account->getAllProperties() as $property) {
637
-			$this->testPropertyScope($property, $allowedScopes, true);
638
-		}
639
-
640
-		$oldData = $this->getUser($account->getUser(), false);
641
-		$this->updateVerificationStatus($account, $oldData);
642
-		$this->checkEmailVerification($account, $oldData);
643
-
644
-		$data = [];
645
-		foreach ($account->getAllProperties() as $property) {
646
-			$data[] = [
647
-				'name' => $property->getName(),
648
-				'value' => $property->getValue(),
649
-				'scope' => $property->getScope(),
650
-				'verified' => $property->getVerified(),
651
-			];
652
-		}
653
-
654
-		$this->updateUser($account->getUser(), $data, true);
655
-	}
69
+    use TAccountsHelper;
70
+
71
+    /** @var  IDBConnection database connection */
72
+    private $connection;
73
+
74
+    /** @var IConfig */
75
+    private $config;
76
+
77
+    /** @var string table name */
78
+    private $table = 'accounts';
79
+
80
+    /** @var string table name */
81
+    private $dataTable = 'accounts_data';
82
+
83
+    /** @var EventDispatcherInterface */
84
+    private $eventDispatcher;
85
+
86
+    /** @var IJobList */
87
+    private $jobList;
88
+
89
+    /** @var LoggerInterface */
90
+    private $logger;
91
+
92
+    public function __construct(IDBConnection $connection,
93
+                                IConfig $config,
94
+                                EventDispatcherInterface $eventDispatcher,
95
+                                IJobList $jobList,
96
+                                LoggerInterface $logger) {
97
+        $this->connection = $connection;
98
+        $this->config = $config;
99
+        $this->eventDispatcher = $eventDispatcher;
100
+        $this->jobList = $jobList;
101
+        $this->logger = $logger;
102
+    }
103
+
104
+    /**
105
+     * @param string $input
106
+     * @return string Provided phone number in E.164 format when it was a valid number
107
+     * @throws InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
108
+     */
109
+    protected function parsePhoneNumber(string $input): string {
110
+        $defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
111
+
112
+        if ($defaultRegion === '') {
113
+            // When no default region is set, only +49… numbers are valid
114
+            if (strpos($input, '+') !== 0) {
115
+                throw new InvalidArgumentException(self::PROPERTY_PHONE);
116
+            }
117
+
118
+            $defaultRegion = 'EN';
119
+        }
120
+
121
+        $phoneUtil = PhoneNumberUtil::getInstance();
122
+        try {
123
+            $phoneNumber = $phoneUtil->parse($input, $defaultRegion);
124
+            if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
125
+                return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
126
+            }
127
+        } catch (NumberParseException $e) {
128
+        }
129
+
130
+        throw new InvalidArgumentException(self::PROPERTY_PHONE);
131
+    }
132
+
133
+    /**
134
+     *
135
+     * @param string $input
136
+     * @return string
137
+     * @throws InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty
138
+     */
139
+    protected function parseWebsite(string $input): string {
140
+        $parts = parse_url($input);
141
+        if (!isset($parts['scheme']) || ($parts['scheme'] !== 'https' && $parts['scheme'] !== 'http')) {
142
+            throw new InvalidArgumentException(self::PROPERTY_WEBSITE);
143
+        }
144
+
145
+        if (!isset($parts['host']) || $parts['host'] === '') {
146
+            throw new InvalidArgumentException(self::PROPERTY_WEBSITE);
147
+        }
148
+
149
+        return $input;
150
+    }
151
+
152
+    /**
153
+     * @param IAccountProperty[] $properties
154
+     */
155
+    protected function testValueLengths(array $properties, bool $throwOnData = false): void {
156
+        foreach ($properties as $property) {
157
+            if (strlen($property->getValue()) > 2048) {
158
+                if ($throwOnData) {
159
+                    throw new InvalidArgumentException();
160
+                } else {
161
+                    $property->setValue('');
162
+                }
163
+            }
164
+        }
165
+    }
166
+
167
+    protected function testPropertyScope(IAccountProperty $property, array $allowedScopes, bool $throwOnData): void {
168
+        if ($throwOnData && !in_array($property->getScope(), $allowedScopes, true)) {
169
+            throw new InvalidArgumentException('scope');
170
+        }
171
+
172
+        if (
173
+            $property->getScope() === self::SCOPE_PRIVATE
174
+            && in_array($property->getName(), [self::PROPERTY_DISPLAYNAME, self::PROPERTY_EMAIL])
175
+        ) {
176
+            if ($throwOnData) {
177
+                // v2-private is not available for these fields
178
+                throw new InvalidArgumentException('scope');
179
+            } else {
180
+                // default to local
181
+                $property->setScope(self::SCOPE_LOCAL);
182
+            }
183
+        } else {
184
+            // migrate scope values to the new format
185
+            // invalid scopes are mapped to a default value
186
+            $property->setScope(AccountProperty::mapScopeToV2($property->getScope()));
187
+        }
188
+    }
189
+
190
+    protected function sanitizePhoneNumberValue(IAccountProperty $property, bool $throwOnData = false) {
191
+        if ($property->getName() !== self::PROPERTY_PHONE) {
192
+            if ($throwOnData) {
193
+                throw new InvalidArgumentException(sprintf('sanitizePhoneNumberValue can only sanitize phone numbers, %s given', $property->getName()));
194
+            }
195
+            return;
196
+        }
197
+        if ($property->getValue() === '') {
198
+            return;
199
+        }
200
+        try {
201
+            $property->setValue($this->parsePhoneNumber($property->getValue()));
202
+        } catch (InvalidArgumentException $e) {
203
+            if ($throwOnData) {
204
+                throw $e;
205
+            }
206
+            $property->setValue('');
207
+        }
208
+    }
209
+
210
+    protected function sanitizeWebsite(IAccountProperty $property, bool $throwOnData = false) {
211
+        if ($property->getName() !== self::PROPERTY_WEBSITE) {
212
+            if ($throwOnData) {
213
+                throw new InvalidArgumentException(sprintf('sanitizeWebsite can only sanitize web domains, %s given', $property->getName()));
214
+            }
215
+        }
216
+        try {
217
+            $property->setValue($this->parseWebsite($property->getValue()));
218
+        } catch (InvalidArgumentException $e) {
219
+            if ($throwOnData) {
220
+                throw $e;
221
+            }
222
+            $property->setValue('');
223
+        }
224
+    }
225
+
226
+    protected function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
227
+        $oldUserData = $this->getUser($user, false);
228
+        $updated = true;
229
+
230
+        if ($oldUserData !== $data) {
231
+            $this->updateExistingUser($user, $data);
232
+        } else {
233
+            // nothing needs to be done if new and old data set are the same
234
+            $updated = false;
235
+        }
236
+
237
+        if ($updated) {
238
+            $this->eventDispatcher->dispatch(
239
+                'OC\AccountManager::userUpdated',
240
+                new GenericEvent($user, $data)
241
+            );
242
+        }
243
+
244
+        return $data;
245
+    }
246
+
247
+    /**
248
+     * delete user from accounts table
249
+     *
250
+     * @param IUser $user
251
+     */
252
+    public function deleteUser(IUser $user) {
253
+        $uid = $user->getUID();
254
+        $query = $this->connection->getQueryBuilder();
255
+        $query->delete($this->table)
256
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
257
+            ->execute();
258
+
259
+        $this->deleteUserData($user);
260
+    }
261
+
262
+    /**
263
+     * delete user from accounts table
264
+     *
265
+     * @param IUser $user
266
+     */
267
+    public function deleteUserData(IUser $user): void {
268
+        $uid = $user->getUID();
269
+        $query = $this->connection->getQueryBuilder();
270
+        $query->delete($this->dataTable)
271
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
272
+            ->execute();
273
+    }
274
+
275
+    /**
276
+     * get stored data from a given user
277
+     */
278
+    protected function getUser(IUser $user, bool $insertIfNotExists = true): array {
279
+        $uid = $user->getUID();
280
+        $query = $this->connection->getQueryBuilder();
281
+        $query->select('data')
282
+            ->from($this->table)
283
+            ->where($query->expr()->eq('uid', $query->createParameter('uid')))
284
+            ->setParameter('uid', $uid);
285
+        $result = $query->executeQuery();
286
+        $accountData = $result->fetchAll();
287
+        $result->closeCursor();
288
+
289
+        if (empty($accountData)) {
290
+            $userData = $this->buildDefaultUserRecord($user);
291
+            if ($insertIfNotExists) {
292
+                $this->insertNewUser($user, $userData);
293
+            }
294
+            return $userData;
295
+        }
296
+
297
+        $userDataArray = $this->importFromJson($accountData[0]['data'], $uid);
298
+        if ($userDataArray === null || $userDataArray === []) {
299
+            return $this->buildDefaultUserRecord($user);
300
+        }
301
+
302
+        return $this->addMissingDefaultValues($userDataArray);
303
+    }
304
+
305
+    public function searchUsers(string $property, array $values): array {
306
+        $chunks = array_chunk($values, 500);
307
+        $query = $this->connection->getQueryBuilder();
308
+        $query->select('*')
309
+            ->from($this->dataTable)
310
+            ->where($query->expr()->eq('name', $query->createNamedParameter($property)))
311
+            ->andWhere($query->expr()->in('value', $query->createParameter('values')));
312
+
313
+        $matches = [];
314
+        foreach ($chunks as $chunk) {
315
+            $query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
316
+            $result = $query->executeQuery();
317
+
318
+            while ($row = $result->fetch()) {
319
+                $matches[$row['uid']] = $row['value'];
320
+            }
321
+            $result->closeCursor();
322
+        }
323
+
324
+        $result = array_merge($matches, $this->searchUsersForRelatedCollection($property, $values));
325
+
326
+        return array_flip($result);
327
+    }
328
+
329
+    protected function searchUsersForRelatedCollection(string $property, array $values): array {
330
+        switch ($property) {
331
+            case IAccountManager::PROPERTY_EMAIL:
332
+                return array_flip($this->searchUsers(IAccountManager::COLLECTION_EMAIL, $values));
333
+            default:
334
+                return [];
335
+        }
336
+    }
337
+
338
+    /**
339
+     * check if we need to ask the server for email verification, if yes we create a cronjob
340
+     *
341
+     */
342
+    protected function checkEmailVerification(IAccount $updatedAccount, array $oldData): void {
343
+        try {
344
+            $property = $updatedAccount->getProperty(self::PROPERTY_EMAIL);
345
+        } catch (PropertyDoesNotExistException $e) {
346
+            return;
347
+        }
348
+        $oldMail = isset($oldData[self::PROPERTY_EMAIL]) ? $oldData[self::PROPERTY_EMAIL]['value']['value'] : '';
349
+        if ($oldMail !== $property->getValue()) {
350
+            $this->jobList->add(VerifyUserData::class,
351
+                [
352
+                    'verificationCode' => '',
353
+                    'data' => $property->getValue(),
354
+                    'type' => self::PROPERTY_EMAIL,
355
+                    'uid' => $updatedAccount->getUser()->getUID(),
356
+                    'try' => 0,
357
+                    'lastRun' => time()
358
+                ]
359
+            );
360
+
361
+
362
+
363
+
364
+            $property->setVerified(self::VERIFICATION_IN_PROGRESS);
365
+        }
366
+    }
367
+
368
+    /**
369
+     * make sure that all expected data are set
370
+     *
371
+     */
372
+    protected function addMissingDefaultValues(array $userData): array {
373
+        foreach ($userData as $i => $value) {
374
+            if (!isset($value['verified'])) {
375
+                $userData[$i]['verified'] = self::NOT_VERIFIED;
376
+            }
377
+        }
378
+
379
+        return $userData;
380
+    }
381
+
382
+    protected function updateVerificationStatus(IAccount $updatedAccount, array $oldData): void {
383
+        static $propertiesVerifiableByLookupServer = [
384
+            self::PROPERTY_TWITTER,
385
+            self::PROPERTY_WEBSITE,
386
+            self::PROPERTY_EMAIL,
387
+        ];
388
+
389
+        foreach ($propertiesVerifiableByLookupServer as $propertyName) {
390
+            try {
391
+                $property = $updatedAccount->getProperty($propertyName);
392
+            } catch (PropertyDoesNotExistException $e) {
393
+                continue;
394
+            }
395
+            $wasVerified = isset($oldData[$propertyName])
396
+                && isset($oldData[$propertyName]['verified'])
397
+                && $oldData[$propertyName]['verified'] === self::VERIFIED;
398
+            if ((!isset($oldData[$propertyName])
399
+                    || !isset($oldData[$propertyName]['value'])
400
+                    || $property->getValue() !== $oldData[$propertyName]['value'])
401
+                && ($property->getVerified() !== self::NOT_VERIFIED
402
+                    || $wasVerified)
403
+                ) {
404
+                $property->setVerified(self::NOT_VERIFIED);
405
+            }
406
+        }
407
+    }
408
+
409
+
410
+    /**
411
+     * add new user to accounts table
412
+     *
413
+     * @param IUser $user
414
+     * @param array $data
415
+     */
416
+    protected function insertNewUser(IUser $user, array $data): void {
417
+        $uid = $user->getUID();
418
+        $jsonEncodedData = $this->prepareJson($data);
419
+        $query = $this->connection->getQueryBuilder();
420
+        $query->insert($this->table)
421
+            ->values(
422
+                [
423
+                    'uid' => $query->createNamedParameter($uid),
424
+                    'data' => $query->createNamedParameter($jsonEncodedData),
425
+                ]
426
+            )
427
+            ->executeStatement();
428
+
429
+        $this->deleteUserData($user);
430
+        $this->writeUserData($user, $data);
431
+    }
432
+
433
+    protected function prepareJson(array $data): string {
434
+        $preparedData = [];
435
+        foreach ($data as $dataRow) {
436
+            $propertyName = $dataRow['name'];
437
+            unset($dataRow['name']);
438
+            if (!$this->isCollection($propertyName)) {
439
+                $preparedData[$propertyName] = $dataRow;
440
+                continue;
441
+            }
442
+            if (!isset($preparedData[$propertyName])) {
443
+                $preparedData[$propertyName] = [];
444
+            }
445
+            $preparedData[$propertyName][] = $dataRow;
446
+        }
447
+        return json_encode($preparedData);
448
+    }
449
+
450
+    protected function importFromJson(string $json, string $userId): ?array {
451
+        $result = [];
452
+        $jsonArray = json_decode($json, true);
453
+        $jsonError = json_last_error();
454
+        if ($jsonError !== JSON_ERROR_NONE) {
455
+            $this->logger->critical(
456
+                'User data of {uid} contained invalid JSON (error {json_error}), hence falling back to a default user record',
457
+                [
458
+                    'uid' => $userId,
459
+                    'json_error' => $jsonError
460
+                ]
461
+            );
462
+            return null;
463
+        }
464
+        foreach ($jsonArray as $propertyName => $row) {
465
+            if (!$this->isCollection($propertyName)) {
466
+                $result[] = array_merge($row, ['name' => $propertyName]);
467
+                continue;
468
+            }
469
+            foreach ($row as $singleRow) {
470
+                $result[] = array_merge($singleRow, ['name' => $propertyName]);
471
+            }
472
+        }
473
+        return $result;
474
+    }
475
+
476
+    /**
477
+     * update existing user in accounts table
478
+     *
479
+     * @param IUser $user
480
+     * @param array $data
481
+     */
482
+    protected function updateExistingUser(IUser $user, array $data): void {
483
+        $uid = $user->getUID();
484
+        $jsonEncodedData = $this->prepareJson($data);
485
+        $query = $this->connection->getQueryBuilder();
486
+        $query->update($this->table)
487
+            ->set('data', $query->createNamedParameter($jsonEncodedData))
488
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
489
+            ->executeStatement();
490
+
491
+        $this->deleteUserData($user);
492
+        $this->writeUserData($user, $data);
493
+    }
494
+
495
+    protected function writeUserData(IUser $user, array $data): void {
496
+        $query = $this->connection->getQueryBuilder();
497
+        $query->insert($this->dataTable)
498
+            ->values(
499
+                [
500
+                    'uid' => $query->createNamedParameter($user->getUID()),
501
+                    'name' => $query->createParameter('name'),
502
+                    'value' => $query->createParameter('value'),
503
+                ]
504
+            );
505
+        $this->writeUserDataProperties($query, $data);
506
+    }
507
+
508
+    protected function writeUserDataProperties(IQueryBuilder $query, array $data): void {
509
+        foreach ($data as $property) {
510
+            if ($property['name'] === self::PROPERTY_AVATAR) {
511
+                continue;
512
+            }
513
+
514
+
515
+            $query->setParameter('name', $property['name'])
516
+                ->setParameter('value', $property['value'] ?? '');
517
+            $query->executeStatement();
518
+        }
519
+    }
520
+
521
+    /**
522
+     * build default user record in case not data set exists yet
523
+     *
524
+     * @param IUser $user
525
+     * @return array
526
+     */
527
+    protected function buildDefaultUserRecord(IUser $user) {
528
+        return [
529
+
530
+            [
531
+                'name' => self::PROPERTY_DISPLAYNAME,
532
+                'value' => $user->getDisplayName(),
533
+                'scope' => self::SCOPE_FEDERATED,
534
+                'verified' => self::NOT_VERIFIED,
535
+            ],
536
+
537
+            [
538
+                'name' => self::PROPERTY_ADDRESS,
539
+                'value' => '',
540
+                'scope' => self::SCOPE_LOCAL,
541
+                'verified' => self::NOT_VERIFIED,
542
+            ],
543
+
544
+            [
545
+                'name' => self::PROPERTY_WEBSITE,
546
+                'value' => '',
547
+                'scope' => self::SCOPE_LOCAL,
548
+                'verified' => self::NOT_VERIFIED,
549
+            ],
550
+
551
+            [
552
+                'name' => self::PROPERTY_EMAIL,
553
+                'value' => $user->getEMailAddress(),
554
+                'scope' => self::SCOPE_FEDERATED,
555
+                'verified' => self::NOT_VERIFIED,
556
+            ],
557
+
558
+            [
559
+                'name' => self::PROPERTY_AVATAR,
560
+                'scope' => self::SCOPE_FEDERATED
561
+            ],
562
+
563
+            [
564
+                'name' => self::PROPERTY_PHONE,
565
+                'value' => '',
566
+                'scope' => self::SCOPE_LOCAL,
567
+                'verified' => self::NOT_VERIFIED,
568
+            ],
569
+
570
+            [
571
+                'name' => self::PROPERTY_TWITTER,
572
+                'value' => '',
573
+                'scope' => self::SCOPE_LOCAL,
574
+                'verified' => self::NOT_VERIFIED,
575
+            ],
576
+
577
+        ];
578
+    }
579
+
580
+    private function arrayDataToCollection(IAccount $account, array $data): IAccountPropertyCollection {
581
+        $collection = $account->getPropertyCollection($data['name']);
582
+
583
+        $p = new AccountProperty(
584
+            $data['name'],
585
+            $data['value'] ?? '',
586
+            $data['scope'] ?? self::SCOPE_LOCAL,
587
+            $data['verified'] ?? self::NOT_VERIFIED,
588
+            ''
589
+        );
590
+        $collection->addProperty($p);
591
+
592
+        return $collection;
593
+    }
594
+
595
+    private function parseAccountData(IUser $user, $data): Account {
596
+        $account = new Account($user);
597
+        foreach ($data as $accountData) {
598
+            if ($this->isCollection($accountData['name'])) {
599
+                $account->setPropertyCollection($this->arrayDataToCollection($account, $accountData));
600
+            } else {
601
+                $account->setProperty($accountData['name'], $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
602
+            }
603
+        }
604
+        return $account;
605
+    }
606
+
607
+    public function getAccount(IUser $user): IAccount {
608
+        return $this->parseAccountData($user, $this->getUser($user));
609
+    }
610
+
611
+    public function updateAccount(IAccount $account): void {
612
+        $this->testValueLengths(iterator_to_array($account->getAllProperties()), true);
613
+        try {
614
+            $property = $account->getProperty(self::PROPERTY_PHONE);
615
+            $this->sanitizePhoneNumberValue($property);
616
+        } catch (PropertyDoesNotExistException $e) {
617
+            //  valid case, nothing to do
618
+        }
619
+
620
+        try {
621
+            $property = $account->getProperty(self::PROPERTY_WEBSITE);
622
+            $this->sanitizeWebsite($property);
623
+        } catch (PropertyDoesNotExistException $e) {
624
+            //  valid case, nothing to do
625
+        }
626
+
627
+        static $allowedScopes = [
628
+            self::SCOPE_PRIVATE,
629
+            self::SCOPE_LOCAL,
630
+            self::SCOPE_FEDERATED,
631
+            self::SCOPE_PUBLISHED,
632
+            self::VISIBILITY_PRIVATE,
633
+            self::VISIBILITY_CONTACTS_ONLY,
634
+            self::VISIBILITY_PUBLIC,
635
+        ];
636
+        foreach ($account->getAllProperties() as $property) {
637
+            $this->testPropertyScope($property, $allowedScopes, true);
638
+        }
639
+
640
+        $oldData = $this->getUser($account->getUser(), false);
641
+        $this->updateVerificationStatus($account, $oldData);
642
+        $this->checkEmailVerification($account, $oldData);
643
+
644
+        $data = [];
645
+        foreach ($account->getAllProperties() as $property) {
646
+            $data[] = [
647
+                'name' => $property->getName(),
648
+                'value' => $property->getValue(),
649
+                'scope' => $property->getScope(),
650
+                'verified' => $property->getVerified(),
651
+            ];
652
+        }
653
+
654
+        $this->updateUser($account->getUser(), $data, true);
655
+    }
656 656
 }
Please login to merge, or discard this patch.
lib/private/Accounts/Hooks.php 2 patches
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -34,46 +34,46 @@
 block discarded – undo
34 34
 
35 35
 class Hooks implements IEventListener {
36 36
 
37
-	/** @var IAccountManager */
38
-	private $accountManager;
39
-	/** @var LoggerInterface */
40
-	private $logger;
37
+    /** @var IAccountManager */
38
+    private $accountManager;
39
+    /** @var LoggerInterface */
40
+    private $logger;
41 41
 
42
-	public function __construct(LoggerInterface $logger, IAccountManager $accountManager) {
43
-		$this->logger = $logger;
44
-		$this->accountManager = $accountManager;
45
-	}
42
+    public function __construct(LoggerInterface $logger, IAccountManager $accountManager) {
43
+        $this->logger = $logger;
44
+        $this->accountManager = $accountManager;
45
+    }
46 46
 
47
-	/**
48
-	 * update accounts table if email address or display name was changed from outside
49
-	 */
50
-	public function changeUserHook(IUser $user, string $feature, $newValue): void {
51
-		$account = $this->accountManager->getAccount($user);
47
+    /**
48
+     * update accounts table if email address or display name was changed from outside
49
+     */
50
+    public function changeUserHook(IUser $user, string $feature, $newValue): void {
51
+        $account = $this->accountManager->getAccount($user);
52 52
 
53
-		try {
54
-			switch ($feature) {
55
-				case 'eMailAddress':
56
-					$property = $account->getProperty(IAccountManager::PROPERTY_EMAIL);
57
-					break;
58
-				case 'displayName':
59
-					$property = $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
60
-					break;
61
-			}
62
-		} catch (PropertyDoesNotExistException $e) {
63
-			$this->logger->debug($e->getMessage(), ['exception' => $e]);
64
-			return;
65
-		}
53
+        try {
54
+            switch ($feature) {
55
+                case 'eMailAddress':
56
+                    $property = $account->getProperty(IAccountManager::PROPERTY_EMAIL);
57
+                    break;
58
+                case 'displayName':
59
+                    $property = $account->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
60
+                    break;
61
+            }
62
+        } catch (PropertyDoesNotExistException $e) {
63
+            $this->logger->debug($e->getMessage(), ['exception' => $e]);
64
+            return;
65
+        }
66 66
 
67
-		if (isset($property) && $property->getValue() !== (string)$newValue) {
68
-			$property->setValue($newValue);
69
-			$this->accountManager->updateAccount($account);
70
-		}
71
-	}
67
+        if (isset($property) && $property->getValue() !== (string)$newValue) {
68
+            $property->setValue($newValue);
69
+            $this->accountManager->updateAccount($account);
70
+        }
71
+    }
72 72
 
73
-	public function handle(Event $event): void {
74
-		if (!$event instanceof UserChangedEvent) {
75
-			return;
76
-		}
77
-		$this->changeUserHook($event->getUser(), $event->getFeature(), $event->getValue());
78
-	}
73
+    public function handle(Event $event): void {
74
+        if (!$event instanceof UserChangedEvent) {
75
+            return;
76
+        }
77
+        $this->changeUserHook($event->getUser(), $event->getFeature(), $event->getValue());
78
+    }
79 79
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
 			return;
65 65
 		}
66 66
 
67
-		if (isset($property) && $property->getValue() !== (string)$newValue) {
67
+		if (isset($property) && $property->getValue() !== (string) $newValue) {
68 68
 			$property->setValue($newValue);
69 69
 			$this->accountManager->updateAccount($account);
70 70
 		}
Please login to merge, or discard this patch.
lib/private/Avatar/AvatarManager.php 1 patch
Indentation   +135 added lines, -135 removed lines patch added patch discarded remove patch
@@ -55,139 +55,139 @@
 block discarded – undo
55 55
  */
56 56
 class AvatarManager implements IAvatarManager {
57 57
 
58
-	/** @var IUserSession */
59
-	private $userSession;
60
-
61
-	/** @var Manager */
62
-	private $userManager;
63
-
64
-	/** @var IAppData */
65
-	private $appData;
66
-
67
-	/** @var IL10N */
68
-	private $l;
69
-
70
-	/** @var LoggerInterface */
71
-	private $logger;
72
-
73
-	/** @var IConfig */
74
-	private $config;
75
-
76
-	/** @var IAccountManager */
77
-	private $accountManager;
78
-
79
-	/** @var KnownUserService */
80
-	private $knownUserService;
81
-
82
-	public function __construct(
83
-			IUserSession $userSession,
84
-			Manager $userManager,
85
-			IAppData $appData,
86
-			IL10N $l,
87
-			LoggerInterface $logger,
88
-			IConfig $config,
89
-			IAccountManager $accountManager,
90
-			KnownUserService $knownUserService
91
-	) {
92
-		$this->userSession = $userSession;
93
-		$this->userManager = $userManager;
94
-		$this->appData = $appData;
95
-		$this->l = $l;
96
-		$this->logger = $logger;
97
-		$this->config = $config;
98
-		$this->accountManager = $accountManager;
99
-		$this->knownUserService = $knownUserService;
100
-	}
101
-
102
-	/**
103
-	 * return a user specific instance of \OCP\IAvatar
104
-	 * @see \OCP\IAvatar
105
-	 * @param string $userId the ownCloud user id
106
-	 * @return \OCP\IAvatar
107
-	 * @throws \Exception In case the username is potentially dangerous
108
-	 * @throws NotFoundException In case there is no user folder yet
109
-	 */
110
-	public function getAvatar(string $userId) : IAvatar {
111
-		$user = $this->userManager->get($userId);
112
-		if ($user === null) {
113
-			throw new \Exception('user does not exist');
114
-		}
115
-
116
-		// sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below)
117
-		$userId = $user->getUID();
118
-
119
-		$requestingUser = null;
120
-		if ($this->userSession !== null) {
121
-			$requestingUser = $this->userSession->getUser();
122
-		}
123
-
124
-		try {
125
-			$folder = $this->appData->getFolder($userId);
126
-		} catch (NotFoundException $e) {
127
-			$folder = $this->appData->newFolder($userId);
128
-		}
129
-
130
-		try {
131
-			$account = $this->accountManager->getAccount($user);
132
-			$avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR);
133
-			$avatarScope = $avatarProperties->getScope();
134
-		} catch (PropertyDoesNotExistException $e) {
135
-			$avatarScope = '';
136
-		}
137
-
138
-		if (
139
-			// v2-private scope hides the avatar from public access and from unknown users
140
-			$avatarScope === IAccountManager::SCOPE_PRIVATE
141
-			&& (
142
-				// accessing from public link
143
-				$requestingUser === null
144
-				// logged in, but unknown to user
145
-				|| !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
146
-			)) {
147
-			// use a placeholder avatar which caches the generated images
148
-			return new PlaceholderAvatar($folder, $user, $this->logger);
149
-		}
150
-
151
-		return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
152
-	}
153
-
154
-	/**
155
-	 * Clear generated avatars
156
-	 */
157
-	public function clearCachedAvatars() {
158
-		$users = $this->config->getUsersForUserValue('avatar', 'generated', 'true');
159
-		foreach ($users as $userId) {
160
-			try {
161
-				$folder = $this->appData->getFolder($userId);
162
-				$folder->delete();
163
-			} catch (NotFoundException $e) {
164
-				$this->logger->debug("No cache for the user $userId. Ignoring...");
165
-			}
166
-			$this->config->setUserValue($userId, 'avatar', 'generated', 'false');
167
-		}
168
-	}
169
-
170
-	public function deleteUserAvatar(string $userId): void {
171
-		try {
172
-			$folder = $this->appData->getFolder($userId);
173
-			$folder->delete();
174
-		} catch (NotFoundException $e) {
175
-			$this->logger->debug("No cache for the user $userId. Ignoring avatar deletion");
176
-		} catch (NotPermittedException $e) {
177
-			$this->logger->error("Unable to delete user avatars for $userId. gnoring avatar deletion");
178
-		} catch (NoUserException $e) {
179
-			$this->logger->debug("User $userId not found. gnoring avatar deletion");
180
-		}
181
-		$this->config->deleteUserValue($userId, 'avatar', 'generated');
182
-	}
183
-
184
-	/**
185
-	 * Returns a GuestAvatar.
186
-	 *
187
-	 * @param string $name The guest name, e.g. "Albert".
188
-	 * @return IAvatar
189
-	 */
190
-	public function getGuestAvatar(string $name): IAvatar {
191
-		return new GuestAvatar($name, $this->logger);
192
-	}
58
+    /** @var IUserSession */
59
+    private $userSession;
60
+
61
+    /** @var Manager */
62
+    private $userManager;
63
+
64
+    /** @var IAppData */
65
+    private $appData;
66
+
67
+    /** @var IL10N */
68
+    private $l;
69
+
70
+    /** @var LoggerInterface */
71
+    private $logger;
72
+
73
+    /** @var IConfig */
74
+    private $config;
75
+
76
+    /** @var IAccountManager */
77
+    private $accountManager;
78
+
79
+    /** @var KnownUserService */
80
+    private $knownUserService;
81
+
82
+    public function __construct(
83
+            IUserSession $userSession,
84
+            Manager $userManager,
85
+            IAppData $appData,
86
+            IL10N $l,
87
+            LoggerInterface $logger,
88
+            IConfig $config,
89
+            IAccountManager $accountManager,
90
+            KnownUserService $knownUserService
91
+    ) {
92
+        $this->userSession = $userSession;
93
+        $this->userManager = $userManager;
94
+        $this->appData = $appData;
95
+        $this->l = $l;
96
+        $this->logger = $logger;
97
+        $this->config = $config;
98
+        $this->accountManager = $accountManager;
99
+        $this->knownUserService = $knownUserService;
100
+    }
101
+
102
+    /**
103
+     * return a user specific instance of \OCP\IAvatar
104
+     * @see \OCP\IAvatar
105
+     * @param string $userId the ownCloud user id
106
+     * @return \OCP\IAvatar
107
+     * @throws \Exception In case the username is potentially dangerous
108
+     * @throws NotFoundException In case there is no user folder yet
109
+     */
110
+    public function getAvatar(string $userId) : IAvatar {
111
+        $user = $this->userManager->get($userId);
112
+        if ($user === null) {
113
+            throw new \Exception('user does not exist');
114
+        }
115
+
116
+        // sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below)
117
+        $userId = $user->getUID();
118
+
119
+        $requestingUser = null;
120
+        if ($this->userSession !== null) {
121
+            $requestingUser = $this->userSession->getUser();
122
+        }
123
+
124
+        try {
125
+            $folder = $this->appData->getFolder($userId);
126
+        } catch (NotFoundException $e) {
127
+            $folder = $this->appData->newFolder($userId);
128
+        }
129
+
130
+        try {
131
+            $account = $this->accountManager->getAccount($user);
132
+            $avatarProperties = $account->getProperty(IAccountManager::PROPERTY_AVATAR);
133
+            $avatarScope = $avatarProperties->getScope();
134
+        } catch (PropertyDoesNotExistException $e) {
135
+            $avatarScope = '';
136
+        }
137
+
138
+        if (
139
+            // v2-private scope hides the avatar from public access and from unknown users
140
+            $avatarScope === IAccountManager::SCOPE_PRIVATE
141
+            && (
142
+                // accessing from public link
143
+                $requestingUser === null
144
+                // logged in, but unknown to user
145
+                || !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
146
+            )) {
147
+            // use a placeholder avatar which caches the generated images
148
+            return new PlaceholderAvatar($folder, $user, $this->logger);
149
+        }
150
+
151
+        return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
152
+    }
153
+
154
+    /**
155
+     * Clear generated avatars
156
+     */
157
+    public function clearCachedAvatars() {
158
+        $users = $this->config->getUsersForUserValue('avatar', 'generated', 'true');
159
+        foreach ($users as $userId) {
160
+            try {
161
+                $folder = $this->appData->getFolder($userId);
162
+                $folder->delete();
163
+            } catch (NotFoundException $e) {
164
+                $this->logger->debug("No cache for the user $userId. Ignoring...");
165
+            }
166
+            $this->config->setUserValue($userId, 'avatar', 'generated', 'false');
167
+        }
168
+    }
169
+
170
+    public function deleteUserAvatar(string $userId): void {
171
+        try {
172
+            $folder = $this->appData->getFolder($userId);
173
+            $folder->delete();
174
+        } catch (NotFoundException $e) {
175
+            $this->logger->debug("No cache for the user $userId. Ignoring avatar deletion");
176
+        } catch (NotPermittedException $e) {
177
+            $this->logger->error("Unable to delete user avatars for $userId. gnoring avatar deletion");
178
+        } catch (NoUserException $e) {
179
+            $this->logger->debug("User $userId not found. gnoring avatar deletion");
180
+        }
181
+        $this->config->deleteUserValue($userId, 'avatar', 'generated');
182
+    }
183
+
184
+    /**
185
+     * Returns a GuestAvatar.
186
+     *
187
+     * @param string $name The guest name, e.g. "Albert".
188
+     * @return IAvatar
189
+     */
190
+    public function getGuestAvatar(string $name): IAvatar {
191
+        return new GuestAvatar($name, $this->logger);
192
+    }
193 193
 }
Please login to merge, or discard this patch.
lib/public/Accounts/IAccountPropertyCollection.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -38,55 +38,55 @@
 block discarded – undo
38 38
  */
39 39
 interface IAccountPropertyCollection extends JsonSerializable {
40 40
 
41
-	/**
42
-	 * retuns the collection name
43
-	 *
44
-	 * @since 22.0.0
45
-	 */
46
-	public function getName(): string;
41
+    /**
42
+     * retuns the collection name
43
+     *
44
+     * @since 22.0.0
45
+     */
46
+    public function getName(): string;
47 47
 
48
-	/**
49
-	 * set properties of this collection
50
-	 *
51
-	 * @param IAccountProperty[] $properties
52
-	 * @throws InvalidArgumentException
53
-	 * @since 22.0.0
54
-	 */
55
-	public function setProperties(array $properties): IAccountPropertyCollection;
48
+    /**
49
+     * set properties of this collection
50
+     *
51
+     * @param IAccountProperty[] $properties
52
+     * @throws InvalidArgumentException
53
+     * @since 22.0.0
54
+     */
55
+    public function setProperties(array $properties): IAccountPropertyCollection;
56 56
 
57
-	/**
58
-	 * @return IAccountProperty[]
59
-	 * @since 22.0.0
60
-	 */
61
-	public function getProperties(): array;
57
+    /**
58
+     * @return IAccountProperty[]
59
+     * @since 22.0.0
60
+     */
61
+    public function getProperties(): array;
62 62
 
63
-	/**
64
-	 * adds a property to this collection
65
-	 *
66
-	 * @throws InvalidArgumentException
67
-	 * @since 22.0.0
68
-	 */
69
-	public function addProperty(IAccountProperty $property): IAccountPropertyCollection;
63
+    /**
64
+     * adds a property to this collection
65
+     *
66
+     * @throws InvalidArgumentException
67
+     * @since 22.0.0
68
+     */
69
+    public function addProperty(IAccountProperty $property): IAccountPropertyCollection;
70 70
 
71
-	/**
72
-	 * adds a property to this collection with only specifying the value
73
-	 *
74
-	 * @throws InvalidArgumentException
75
-	 * @since 22.0.0
76
-	 */
77
-	public function addPropertyWithDefaults(string $value): IAccountPropertyCollection;
71
+    /**
72
+     * adds a property to this collection with only specifying the value
73
+     *
74
+     * @throws InvalidArgumentException
75
+     * @since 22.0.0
76
+     */
77
+    public function addPropertyWithDefaults(string $value): IAccountPropertyCollection;
78 78
 
79
-	/**
80
-	 * removes a property of this collection
81
-	 *
82
-	 * @since 22.0.0
83
-	 */
84
-	public function removeProperty(IAccountProperty $property): IAccountPropertyCollection;
79
+    /**
80
+     * removes a property of this collection
81
+     *
82
+     * @since 22.0.0
83
+     */
84
+    public function removeProperty(IAccountProperty $property): IAccountPropertyCollection;
85 85
 
86
-	/**
87
-	 * removes a property identified by its value
88
-	 *
89
-	 * @since 22.0.0
90
-	 */
91
-	public function removePropertyByValue(string $value): IAccountPropertyCollection;
86
+    /**
87
+     * removes a property identified by its value
88
+     *
89
+     * @since 22.0.0
90
+     */
91
+    public function removePropertyByValue(string $value): IAccountPropertyCollection;
92 92
 }
Please login to merge, or discard this patch.
lib/public/Accounts/IAccount.php 1 patch
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -36,90 +36,90 @@
 block discarded – undo
36 36
  */
37 37
 interface IAccount extends \JsonSerializable {
38 38
 
39
-	/**
40
-	 * Set a property with data
41
-	 *
42
-	 * @since 15.0.0
43
-	 *
44
-	 * @param string $property  Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
45
-	 * @param string $value
46
-	 * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
47
-	 * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
48
-	 * @param string $verificationData Optional, defaults to empty string. Since @22.0.0.
49
-	 * @return IAccount
50
-	 */
51
-	public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount;
39
+    /**
40
+     * Set a property with data
41
+     *
42
+     * @since 15.0.0
43
+     *
44
+     * @param string $property  Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
45
+     * @param string $value
46
+     * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
47
+     * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
48
+     * @param string $verificationData Optional, defaults to empty string. Since @22.0.0.
49
+     * @return IAccount
50
+     */
51
+    public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount;
52 52
 
53
-	/**
54
-	 * Get a property by its key
55
-	 *
56
-	 * @since 15.0.0
57
-	 *
58
-	 * @param string $property Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
59
-	 * @return IAccountProperty
60
-	 * @throws PropertyDoesNotExistException
61
-	 */
62
-	public function getProperty(string $property): IAccountProperty;
53
+    /**
54
+     * Get a property by its key
55
+     *
56
+     * @since 15.0.0
57
+     *
58
+     * @param string $property Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
59
+     * @return IAccountProperty
60
+     * @throws PropertyDoesNotExistException
61
+     */
62
+    public function getProperty(string $property): IAccountProperty;
63 63
 
64
-	/**
65
-	 * Get all properties of an account. Array indices are property names.
66
-	 * Values from IAccountPropertyCollections are not included in the return
67
-	 * array.
68
-	 *
69
-	 * @since 15.0.0
70
-	 * @deprecated 22.0.0 use getAllProperties()
71
-	 */
72
-	public function getProperties(): array;
64
+    /**
65
+     * Get all properties of an account. Array indices are property names.
66
+     * Values from IAccountPropertyCollections are not included in the return
67
+     * array.
68
+     *
69
+     * @since 15.0.0
70
+     * @deprecated 22.0.0 use getAllProperties()
71
+     */
72
+    public function getProperties(): array;
73 73
 
74
-	/**
75
-	 * Get all properties of an account. Array indices are numeric. To get
76
-	 * the property name, call getName() against the value.
77
-	 *
78
-	 * IAccountPropertyCollections are being flattened into an IAccountProperty
79
-	 * for each value.
80
-	 *
81
-	 * @since 22.0.0
82
-	 *
83
-	 * @return Generator<int, IAccountProperty>
84
-	 */
85
-	public function getAllProperties(): Generator;
74
+    /**
75
+     * Get all properties of an account. Array indices are numeric. To get
76
+     * the property name, call getName() against the value.
77
+     *
78
+     * IAccountPropertyCollections are being flattened into an IAccountProperty
79
+     * for each value.
80
+     *
81
+     * @since 22.0.0
82
+     *
83
+     * @return Generator<int, IAccountProperty>
84
+     */
85
+    public function getAllProperties(): Generator;
86 86
 
87
-	/**
88
-	 * Set a property collection (multi-value properties)
89
-	 *
90
-	 * @since 22.0.0
91
-	 */
92
-	public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount;
87
+    /**
88
+     * Set a property collection (multi-value properties)
89
+     *
90
+     * @since 22.0.0
91
+     */
92
+    public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount;
93 93
 
94
-	/**
95
-	 * Returns the requestes propery collection (multi-value properties)
96
-	 *
97
-	 * @throws PropertyDoesNotExistException against invalid collection name
98
-	 * @since 22.0.0
99
-	 */
100
-	public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection;
94
+    /**
95
+     * Returns the requestes propery collection (multi-value properties)
96
+     *
97
+     * @throws PropertyDoesNotExistException against invalid collection name
98
+     * @since 22.0.0
99
+     */
100
+    public function getPropertyCollection(string $propertyCollectionName): IAccountPropertyCollection;
101 101
 
102
-	/**
103
-	 * Get all properties that match the provided filters for scope and verification status
104
-	 *
105
-	 * Since 22.0.0 values from IAccountPropertyCollection are included, but also
106
-	 * as IAccountProperty instances. They for properties of IAccountPropertyCollection are
107
-	 * suffixed incrementally, i.e. #0, #1 ... #n – the numbers have no further meaning.
108
-	 *
109
-	 * @since 15.0.0
110
-	 *
111
-	 * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
112
-	 * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
113
-	 * @return IAccountProperty[]
114
-	 */
115
-	public function getFilteredProperties(string $scope = null, string $verified = null): array;
102
+    /**
103
+     * Get all properties that match the provided filters for scope and verification status
104
+     *
105
+     * Since 22.0.0 values from IAccountPropertyCollection are included, but also
106
+     * as IAccountProperty instances. They for properties of IAccountPropertyCollection are
107
+     * suffixed incrementally, i.e. #0, #1 ... #n – the numbers have no further meaning.
108
+     *
109
+     * @since 15.0.0
110
+     *
111
+     * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
112
+     * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
113
+     * @return IAccountProperty[]
114
+     */
115
+    public function getFilteredProperties(string $scope = null, string $verified = null): array;
116 116
 
117
-	/**
118
-	 * Get the related user for the account data
119
-	 *
120
-	 * @since 15.0.0
121
-	 *
122
-	 * @return IUser
123
-	 */
124
-	public function getUser(): IUser;
117
+    /**
118
+     * Get the related user for the account data
119
+     *
120
+     * @since 15.0.0
121
+     *
122
+     * @return IUser
123
+     */
124
+    public function getUser(): IUser;
125 125
 }
Please login to merge, or discard this patch.
lib/base.php 1 patch
Indentation   +1000 added lines, -1000 removed lines patch added patch discarded remove patch
@@ -78,1006 +78,1006 @@
 block discarded – undo
78 78
  * OC_autoload!
79 79
  */
80 80
 class OC {
81
-	/**
82
-	 * Associative array for autoloading. classname => filename
83
-	 */
84
-	public static $CLASSPATH = [];
85
-	/**
86
-	 * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
87
-	 */
88
-	public static $SERVERROOT = '';
89
-	/**
90
-	 * the current request path relative to the Nextcloud root (e.g. files/index.php)
91
-	 */
92
-	private static $SUBURI = '';
93
-	/**
94
-	 * the Nextcloud root path for http requests (e.g. nextcloud/)
95
-	 */
96
-	public static $WEBROOT = '';
97
-	/**
98
-	 * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
99
-	 * web path in 'url'
100
-	 */
101
-	public static $APPSROOTS = [];
102
-
103
-	/**
104
-	 * @var string
105
-	 */
106
-	public static $configDir;
107
-
108
-	/**
109
-	 * requested app
110
-	 */
111
-	public static $REQUESTEDAPP = '';
112
-
113
-	/**
114
-	 * check if Nextcloud runs in cli mode
115
-	 */
116
-	public static $CLI = false;
117
-
118
-	/**
119
-	 * @var \OC\Autoloader $loader
120
-	 */
121
-	public static $loader = null;
122
-
123
-	/** @var \Composer\Autoload\ClassLoader $composerAutoloader */
124
-	public static $composerAutoloader = null;
125
-
126
-	/**
127
-	 * @var \OC\Server
128
-	 */
129
-	public static $server = null;
130
-
131
-	/**
132
-	 * @var \OC\Config
133
-	 */
134
-	private static $config = null;
135
-
136
-	/**
137
-	 * @throws \RuntimeException when the 3rdparty directory is missing or
138
-	 * the app path list is empty or contains an invalid path
139
-	 */
140
-	public static function initPaths() {
141
-		if (defined('PHPUNIT_CONFIG_DIR')) {
142
-			self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
143
-		} elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
144
-			self::$configDir = OC::$SERVERROOT . '/tests/config/';
145
-		} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
146
-			self::$configDir = rtrim($dir, '/') . '/';
147
-		} else {
148
-			self::$configDir = OC::$SERVERROOT . '/config/';
149
-		}
150
-		self::$config = new \OC\Config(self::$configDir);
151
-
152
-		OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
153
-		/**
154
-		 * FIXME: The following lines are required because we can't yet instantiate
155
-		 *        \OC::$server->getRequest() since \OC::$server does not yet exist.
156
-		 */
157
-		$params = [
158
-			'server' => [
159
-				'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
160
-				'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
161
-			],
162
-		];
163
-		$fakeRequest = new \OC\AppFramework\Http\Request($params, new \OC\Security\SecureRandom(), new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
164
-		$scriptName = $fakeRequest->getScriptName();
165
-		if (substr($scriptName, -1) == '/') {
166
-			$scriptName .= 'index.php';
167
-			//make sure suburi follows the same rules as scriptName
168
-			if (substr(OC::$SUBURI, -9) != 'index.php') {
169
-				if (substr(OC::$SUBURI, -1) != '/') {
170
-					OC::$SUBURI = OC::$SUBURI . '/';
171
-				}
172
-				OC::$SUBURI = OC::$SUBURI . 'index.php';
173
-			}
174
-		}
175
-
176
-
177
-		if (OC::$CLI) {
178
-			OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
179
-		} else {
180
-			if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
181
-				OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
182
-
183
-				if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
184
-					OC::$WEBROOT = '/' . OC::$WEBROOT;
185
-				}
186
-			} else {
187
-				// The scriptName is not ending with OC::$SUBURI
188
-				// This most likely means that we are calling from CLI.
189
-				// However some cron jobs still need to generate
190
-				// a web URL, so we use overwritewebroot as a fallback.
191
-				OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
192
-			}
193
-
194
-			// Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
195
-			// slash which is required by URL generation.
196
-			if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
197
-					substr($_SERVER['REQUEST_URI'], -1) !== '/') {
198
-				header('Location: '.\OC::$WEBROOT.'/');
199
-				exit();
200
-			}
201
-		}
202
-
203
-		// search the apps folder
204
-		$config_paths = self::$config->getValue('apps_paths', []);
205
-		if (!empty($config_paths)) {
206
-			foreach ($config_paths as $paths) {
207
-				if (isset($paths['url']) && isset($paths['path'])) {
208
-					$paths['url'] = rtrim($paths['url'], '/');
209
-					$paths['path'] = rtrim($paths['path'], '/');
210
-					OC::$APPSROOTS[] = $paths;
211
-				}
212
-			}
213
-		} elseif (file_exists(OC::$SERVERROOT . '/apps')) {
214
-			OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
215
-		}
216
-
217
-		if (empty(OC::$APPSROOTS)) {
218
-			throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
219
-				. '. You can also configure the location in the config.php file.');
220
-		}
221
-		$paths = [];
222
-		foreach (OC::$APPSROOTS as $path) {
223
-			$paths[] = $path['path'];
224
-			if (!is_dir($path['path'])) {
225
-				throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
226
-					. ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
227
-			}
228
-		}
229
-
230
-		// set the right include path
231
-		set_include_path(
232
-			implode(PATH_SEPARATOR, $paths)
233
-		);
234
-	}
235
-
236
-	public static function checkConfig() {
237
-		$l = \OC::$server->getL10N('lib');
238
-
239
-		// Create config if it does not already exist
240
-		$configFilePath = self::$configDir .'/config.php';
241
-		if (!file_exists($configFilePath)) {
242
-			@touch($configFilePath);
243
-		}
244
-
245
-		// Check if config is writable
246
-		$configFileWritable = is_writable($configFilePath);
247
-		if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
248
-			|| !$configFileWritable && \OCP\Util::needUpgrade()) {
249
-			$urlGenerator = \OC::$server->getURLGenerator();
250
-
251
-			if (self::$CLI) {
252
-				echo $l->t('Cannot write into "config" directory!')."\n";
253
-				echo $l->t('This can usually be fixed by giving the webserver write access to the config directory.')."\n";
254
-				echo "\n";
255
-				echo $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.')."\n";
256
-				echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
257
-				exit;
258
-			} else {
259
-				OC_Template::printErrorPage(
260
-					$l->t('Cannot write into "config" directory!'),
261
-					$l->t('This can usually be fixed by giving the webserver write access to the config directory.') . ' '
262
-					. $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
263
-					. $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
264
-					503
265
-				);
266
-			}
267
-		}
268
-	}
269
-
270
-	public static function checkInstalled(\OC\SystemConfig $systemConfig) {
271
-		if (defined('OC_CONSOLE')) {
272
-			return;
273
-		}
274
-		// Redirect to installer if not installed
275
-		if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
276
-			if (OC::$CLI) {
277
-				throw new Exception('Not installed');
278
-			} else {
279
-				$url = OC::$WEBROOT . '/index.php';
280
-				header('Location: ' . $url);
281
-			}
282
-			exit();
283
-		}
284
-	}
285
-
286
-	public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig) {
287
-		// Allow ajax update script to execute without being stopped
288
-		if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
289
-			// send http status 503
290
-			http_response_code(503);
291
-			header('Retry-After: 120');
292
-
293
-			// render error page
294
-			$template = new OC_Template('', 'update.user', 'guest');
295
-			OC_Util::addScript('dist/maintenance');
296
-			OC_Util::addStyle('core', 'guest');
297
-			$template->printPage();
298
-			die();
299
-		}
300
-	}
301
-
302
-	/**
303
-	 * Prints the upgrade page
304
-	 *
305
-	 * @param \OC\SystemConfig $systemConfig
306
-	 */
307
-	private static function printUpgradePage(\OC\SystemConfig $systemConfig) {
308
-		$disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
309
-		$tooBig = false;
310
-		if (!$disableWebUpdater) {
311
-			$apps = \OC::$server->getAppManager();
312
-			if ($apps->isInstalled('user_ldap')) {
313
-				$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
314
-
315
-				$result = $qb->select($qb->func()->count('*', 'user_count'))
316
-					->from('ldap_user_mapping')
317
-					->execute();
318
-				$row = $result->fetch();
319
-				$result->closeCursor();
320
-
321
-				$tooBig = ($row['user_count'] > 50);
322
-			}
323
-			if (!$tooBig && $apps->isInstalled('user_saml')) {
324
-				$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
325
-
326
-				$result = $qb->select($qb->func()->count('*', 'user_count'))
327
-					->from('user_saml_users')
328
-					->execute();
329
-				$row = $result->fetch();
330
-				$result->closeCursor();
331
-
332
-				$tooBig = ($row['user_count'] > 50);
333
-			}
334
-			if (!$tooBig) {
335
-				// count users
336
-				$stats = \OC::$server->getUserManager()->countUsers();
337
-				$totalUsers = array_sum($stats);
338
-				$tooBig = ($totalUsers > 50);
339
-			}
340
-		}
341
-		$ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
342
-			$_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
343
-
344
-		if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
345
-			// send http status 503
346
-			http_response_code(503);
347
-			header('Retry-After: 120');
348
-
349
-			// render error page
350
-			$template = new OC_Template('', 'update.use-cli', 'guest');
351
-			$template->assign('productName', 'nextcloud'); // for now
352
-			$template->assign('version', OC_Util::getVersionString());
353
-			$template->assign('tooBig', $tooBig);
354
-
355
-			$template->printPage();
356
-			die();
357
-		}
358
-
359
-		// check whether this is a core update or apps update
360
-		$installedVersion = $systemConfig->getValue('version', '0.0.0');
361
-		$currentVersion = implode('.', \OCP\Util::getVersion());
362
-
363
-		// if not a core upgrade, then it's apps upgrade
364
-		$isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
365
-
366
-		$oldTheme = $systemConfig->getValue('theme');
367
-		$systemConfig->setValue('theme', '');
368
-		OC_Util::addScript('update');
369
-
370
-		/** @var \OC\App\AppManager $appManager */
371
-		$appManager = \OC::$server->getAppManager();
372
-
373
-		$tmpl = new OC_Template('', 'update.admin', 'guest');
374
-		$tmpl->assign('version', OC_Util::getVersionString());
375
-		$tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
376
-
377
-		// get third party apps
378
-		$ocVersion = \OCP\Util::getVersion();
379
-		$ocVersion = implode('.', $ocVersion);
380
-		$incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
381
-		$incompatibleShippedApps = [];
382
-		foreach ($incompatibleApps as $appInfo) {
383
-			if ($appManager->isShipped($appInfo['id'])) {
384
-				$incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
385
-			}
386
-		}
387
-
388
-		if (!empty($incompatibleShippedApps)) {
389
-			$l = \OC::$server->getL10N('core');
390
-			$hint = $l->t('The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
391
-			throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
392
-		}
393
-
394
-		$tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
395
-		$tmpl->assign('incompatibleAppsList', $incompatibleApps);
396
-		try {
397
-			$defaults = new \OC_Defaults();
398
-			$tmpl->assign('productName', $defaults->getName());
399
-		} catch (Throwable $error) {
400
-			$tmpl->assign('productName', 'Nextcloud');
401
-		}
402
-		$tmpl->assign('oldTheme', $oldTheme);
403
-		$tmpl->printPage();
404
-	}
405
-
406
-	public static function initSession() {
407
-		if (self::$server->getRequest()->getServerProtocol() === 'https') {
408
-			ini_set('session.cookie_secure', 'true');
409
-		}
410
-
411
-		// prevents javascript from accessing php session cookies
412
-		ini_set('session.cookie_httponly', 'true');
413
-
414
-		// set the cookie path to the Nextcloud directory
415
-		$cookie_path = OC::$WEBROOT ? : '/';
416
-		ini_set('session.cookie_path', $cookie_path);
417
-
418
-		// Let the session name be changed in the initSession Hook
419
-		$sessionName = OC_Util::getInstanceId();
420
-
421
-		try {
422
-			// set the session name to the instance id - which is unique
423
-			$session = new \OC\Session\Internal($sessionName);
424
-
425
-			$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
426
-			$session = $cryptoWrapper->wrapSession($session);
427
-			self::$server->setSession($session);
428
-
429
-			// if session can't be started break with http 500 error
430
-		} catch (Exception $e) {
431
-			\OC::$server->getLogger()->logException($e, ['app' => 'base']);
432
-			//show the user a detailed error page
433
-			OC_Template::printExceptionErrorPage($e, 500);
434
-			die();
435
-		}
436
-
437
-		$sessionLifeTime = self::getSessionLifeTime();
438
-
439
-		// session timeout
440
-		if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
441
-			if (isset($_COOKIE[session_name()])) {
442
-				setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
443
-			}
444
-			\OC::$server->getUserSession()->logout();
445
-		}
446
-
447
-		$session->set('LAST_ACTIVITY', time());
448
-	}
449
-
450
-	/**
451
-	 * @return string
452
-	 */
453
-	private static function getSessionLifeTime() {
454
-		return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
455
-	}
456
-
457
-	/**
458
-	 * Try to set some values to the required Nextcloud default
459
-	 */
460
-	public static function setRequiredIniValues() {
461
-		@ini_set('default_charset', 'UTF-8');
462
-		@ini_set('gd.jpeg_ignore_warning', '1');
463
-	}
464
-
465
-	/**
466
-	 * Send the same site cookies
467
-	 */
468
-	private static function sendSameSiteCookies() {
469
-		$cookieParams = session_get_cookie_params();
470
-		$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
471
-		$policies = [
472
-			'lax',
473
-			'strict',
474
-		];
475
-
476
-		// Append __Host to the cookie if it meets the requirements
477
-		$cookiePrefix = '';
478
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
479
-			$cookiePrefix = '__Host-';
480
-		}
481
-
482
-		foreach ($policies as $policy) {
483
-			header(
484
-				sprintf(
485
-					'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
486
-					$cookiePrefix,
487
-					$policy,
488
-					$cookieParams['path'],
489
-					$policy
490
-				),
491
-				false
492
-			);
493
-		}
494
-	}
495
-
496
-	/**
497
-	 * Same Site cookie to further mitigate CSRF attacks. This cookie has to
498
-	 * be set in every request if cookies are sent to add a second level of
499
-	 * defense against CSRF.
500
-	 *
501
-	 * If the cookie is not sent this will set the cookie and reload the page.
502
-	 * We use an additional cookie since we want to protect logout CSRF and
503
-	 * also we can't directly interfere with PHP's session mechanism.
504
-	 */
505
-	private static function performSameSiteCookieProtection(\OCP\IConfig $config) {
506
-		$request = \OC::$server->getRequest();
507
-
508
-		// Some user agents are notorious and don't really properly follow HTTP
509
-		// specifications. For those, have an automated opt-out. Since the protection
510
-		// for remote.php is applied in base.php as starting point we need to opt out
511
-		// here.
512
-		$incompatibleUserAgents = $config->getSystemValue('csrf.optout');
513
-
514
-		// Fallback, if csrf.optout is unset
515
-		if (!is_array($incompatibleUserAgents)) {
516
-			$incompatibleUserAgents = [
517
-				// OS X Finder
518
-				'/^WebDAVFS/',
519
-				// Windows webdav drive
520
-				'/^Microsoft-WebDAV-MiniRedir/',
521
-			];
522
-		}
523
-
524
-		if ($request->isUserAgent($incompatibleUserAgents)) {
525
-			return;
526
-		}
527
-
528
-		if (count($_COOKIE) > 0) {
529
-			$requestUri = $request->getScriptName();
530
-			$processingScript = explode('/', $requestUri);
531
-			$processingScript = $processingScript[count($processingScript) - 1];
532
-
533
-			// index.php routes are handled in the middleware
534
-			if ($processingScript === 'index.php') {
535
-				return;
536
-			}
537
-
538
-			// All other endpoints require the lax and the strict cookie
539
-			if (!$request->passesStrictCookieCheck()) {
540
-				self::sendSameSiteCookies();
541
-				// Debug mode gets access to the resources without strict cookie
542
-				// due to the fact that the SabreDAV browser also lives there.
543
-				if (!$config->getSystemValue('debug', false)) {
544
-					http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
545
-					exit();
546
-				}
547
-			}
548
-		} elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
549
-			self::sendSameSiteCookies();
550
-		}
551
-	}
552
-
553
-	public static function init() {
554
-		// calculate the root directories
555
-		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
556
-
557
-		// register autoloader
558
-		$loaderStart = microtime(true);
559
-		require_once __DIR__ . '/autoloader.php';
560
-		self::$loader = new \OC\Autoloader([
561
-			OC::$SERVERROOT . '/lib/private/legacy',
562
-		]);
563
-		if (defined('PHPUNIT_RUN')) {
564
-			self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
565
-		}
566
-		spl_autoload_register([self::$loader, 'load']);
567
-		$loaderEnd = microtime(true);
568
-
569
-		self::$CLI = (php_sapi_name() == 'cli');
570
-
571
-		// Add default composer PSR-4 autoloader
572
-		self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
573
-
574
-		try {
575
-			self::initPaths();
576
-			// setup 3rdparty autoloader
577
-			$vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
578
-			if (!file_exists($vendorAutoLoad)) {
579
-				throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
580
-			}
581
-			require_once $vendorAutoLoad;
582
-		} catch (\RuntimeException $e) {
583
-			if (!self::$CLI) {
584
-				http_response_code(503);
585
-			}
586
-			// we can't use the template error page here, because this needs the
587
-			// DI container which isn't available yet
588
-			print($e->getMessage());
589
-			exit();
590
-		}
591
-
592
-		// setup the basic server
593
-		self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
594
-		self::$server->boot();
595
-		$eventLogger = \OC::$server->getEventLogger();
596
-		$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
597
-		$eventLogger->start('boot', 'Initialize');
598
-
599
-		// Override php.ini and log everything if we're troubleshooting
600
-		if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
601
-			error_reporting(E_ALL);
602
-		}
603
-
604
-		// Don't display errors and log them
605
-		@ini_set('display_errors', '0');
606
-		@ini_set('log_errors', '1');
607
-
608
-		if (!date_default_timezone_set('UTC')) {
609
-			throw new \RuntimeException('Could not set timezone to UTC');
610
-		}
611
-
612
-		//try to configure php to enable big file uploads.
613
-		//this doesn´t work always depending on the webserver and php configuration.
614
-		//Let´s try to overwrite some defaults anyway
615
-
616
-		//try to set the maximum execution time to 60min
617
-		if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
618
-			@set_time_limit(3600);
619
-		}
620
-		@ini_set('max_execution_time', '3600');
621
-		@ini_set('max_input_time', '3600');
622
-
623
-		self::setRequiredIniValues();
624
-		self::handleAuthHeaders();
625
-		$systemConfig = \OC::$server->get(\OC\SystemConfig::class);
626
-		self::registerAutoloaderCache($systemConfig);
627
-
628
-		// initialize intl fallback if necessary
629
-		OC_Util::isSetLocaleWorking();
630
-
631
-		$config = \OC::$server->get(\OCP\IConfig::class);
632
-		if (!defined('PHPUNIT_RUN')) {
633
-			OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
634
-			$debug = $config->getSystemValue('debug', false);
635
-			OC\Log\ErrorHandler::register($debug);
636
-		}
637
-
638
-		/** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
639
-		$bootstrapCoordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
640
-		$bootstrapCoordinator->runInitialRegistration();
641
-
642
-		$eventLogger->start('init_session', 'Initialize session');
643
-		OC_App::loadApps(['session']);
644
-		if (!self::$CLI) {
645
-			self::initSession();
646
-		}
647
-		$eventLogger->end('init_session');
648
-		self::checkConfig();
649
-		self::checkInstalled($systemConfig);
650
-
651
-		OC_Response::addSecurityHeaders();
652
-
653
-		self::performSameSiteCookieProtection($config);
654
-
655
-		if (!defined('OC_CONSOLE')) {
656
-			$errors = OC_Util::checkServer($systemConfig);
657
-			if (count($errors) > 0) {
658
-				if (!self::$CLI) {
659
-					http_response_code(503);
660
-					OC_Util::addStyle('guest');
661
-					try {
662
-						OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
663
-						exit;
664
-					} catch (\Exception $e) {
665
-						// In case any error happens when showing the error page, we simply fall back to posting the text.
666
-						// This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
667
-					}
668
-				}
669
-
670
-				// Convert l10n string into regular string for usage in database
671
-				$staticErrors = [];
672
-				foreach ($errors as $error) {
673
-					echo $error['error'] . "\n";
674
-					echo $error['hint'] . "\n\n";
675
-					$staticErrors[] = [
676
-						'error' => (string)$error['error'],
677
-						'hint' => (string)$error['hint'],
678
-					];
679
-				}
680
-
681
-				try {
682
-					$config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
683
-				} catch (\Exception $e) {
684
-					echo('Writing to database failed');
685
-				}
686
-				exit(1);
687
-			} elseif (self::$CLI && $config->getSystemValue('installed', false)) {
688
-				$config->deleteAppValue('core', 'cronErrors');
689
-			}
690
-		}
691
-		//try to set the session lifetime
692
-		$sessionLifeTime = self::getSessionLifeTime();
693
-		@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
694
-
695
-		// User and Groups
696
-		if (!$systemConfig->getValue("installed", false)) {
697
-			self::$server->getSession()->set('user_id', '');
698
-		}
699
-
700
-		OC_User::useBackend(new \OC\User\Database());
701
-		\OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
702
-
703
-		// Subscribe to the hook
704
-		\OCP\Util::connectHook(
705
-			'\OCA\Files_Sharing\API\Server2Server',
706
-			'preLoginNameUsedAsUserName',
707
-			'\OC\User\Database',
708
-			'preLoginNameUsedAsUserName'
709
-		);
710
-
711
-		//setup extra user backends
712
-		if (!\OCP\Util::needUpgrade()) {
713
-			OC_User::setupBackends();
714
-		} else {
715
-			// Run upgrades in incognito mode
716
-			OC_User::setIncognitoMode(true);
717
-		}
718
-
719
-		self::registerCleanupHooks($systemConfig);
720
-		self::registerFilesystemHooks();
721
-		self::registerShareHooks($systemConfig);
722
-		self::registerEncryptionWrapperAndHooks();
723
-		self::registerAccountHooks();
724
-		self::registerResourceCollectionHooks();
725
-		self::registerAppRestrictionsHooks();
726
-
727
-		// Make sure that the application class is not loaded before the database is setup
728
-		if ($systemConfig->getValue("installed", false)) {
729
-			OC_App::loadApp('settings');
730
-		}
731
-
732
-		//make sure temporary files are cleaned up
733
-		$tmpManager = \OC::$server->getTempManager();
734
-		register_shutdown_function([$tmpManager, 'clean']);
735
-		$lockProvider = \OC::$server->getLockingProvider();
736
-		register_shutdown_function([$lockProvider, 'releaseAll']);
737
-
738
-		// Check whether the sample configuration has been copied
739
-		if ($systemConfig->getValue('copied_sample_config', false)) {
740
-			$l = \OC::$server->getL10N('lib');
741
-			OC_Template::printErrorPage(
742
-				$l->t('Sample configuration detected'),
743
-				$l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php'),
744
-				503
745
-			);
746
-			return;
747
-		}
748
-
749
-		$request = \OC::$server->getRequest();
750
-		$host = $request->getInsecureServerHost();
751
-		/**
752
-		 * if the host passed in headers isn't trusted
753
-		 * FIXME: Should not be in here at all :see_no_evil:
754
-		 */
755
-		if (!OC::$CLI
756
-			&& !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
757
-			&& $config->getSystemValue('installed', false)
758
-		) {
759
-			// Allow access to CSS resources
760
-			$isScssRequest = false;
761
-			if (strpos($request->getPathInfo(), '/css/') === 0) {
762
-				$isScssRequest = true;
763
-			}
764
-
765
-			if (substr($request->getRequestUri(), -11) === '/status.php') {
766
-				http_response_code(400);
767
-				header('Content-Type: application/json');
768
-				echo '{"error": "Trusted domain error.", "code": 15}';
769
-				exit();
770
-			}
771
-
772
-			if (!$isScssRequest) {
773
-				http_response_code(400);
774
-
775
-				\OC::$server->getLogger()->info(
776
-					'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
777
-					[
778
-						'app' => 'core',
779
-						'remoteAddress' => $request->getRemoteAddress(),
780
-						'host' => $host,
781
-					]
782
-				);
783
-
784
-				$tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
785
-				$tmpl->assign('docUrl', \OC::$server->getURLGenerator()->linkToDocs('admin-trusted-domains'));
786
-				$tmpl->printPage();
787
-
788
-				exit();
789
-			}
790
-		}
791
-		$eventLogger->end('boot');
792
-	}
793
-
794
-	/**
795
-	 * register hooks for the cleanup of cache and bruteforce protection
796
-	 */
797
-	public static function registerCleanupHooks(\OC\SystemConfig $systemConfig) {
798
-		//don't try to do this before we are properly setup
799
-		if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
800
-
801
-			// NOTE: This will be replaced to use OCP
802
-			$userSession = self::$server->getUserSession();
803
-			$userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
804
-				if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
805
-					// reset brute force delay for this IP address and username
806
-					$uid = \OC::$server->getUserSession()->getUser()->getUID();
807
-					$request = \OC::$server->getRequest();
808
-					$throttler = \OC::$server->getBruteForceThrottler();
809
-					$throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
810
-				}
811
-
812
-				try {
813
-					$cache = new \OC\Cache\File();
814
-					$cache->gc();
815
-				} catch (\OC\ServerNotAvailableException $e) {
816
-					// not a GC exception, pass it on
817
-					throw $e;
818
-				} catch (\OC\ForbiddenException $e) {
819
-					// filesystem blocked for this request, ignore
820
-				} catch (\Exception $e) {
821
-					// a GC exception should not prevent users from using OC,
822
-					// so log the exception
823
-					\OC::$server->getLogger()->logException($e, [
824
-						'message' => 'Exception when running cache gc.',
825
-						'level' => ILogger::WARN,
826
-						'app' => 'core',
827
-					]);
828
-				}
829
-			});
830
-		}
831
-	}
832
-
833
-	private static function registerEncryptionWrapperAndHooks() {
834
-		$manager = self::$server->getEncryptionManager();
835
-		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
836
-
837
-		$enabled = $manager->isEnabled();
838
-		if ($enabled) {
839
-			\OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
840
-			\OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
841
-			\OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
842
-			\OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
843
-		}
844
-	}
845
-
846
-	private static function registerAccountHooks() {
847
-		/** @var IEventDispatcher $dispatcher */
848
-		$dispatcher = \OC::$server->get(IEventDispatcher::class);
849
-		$dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
850
-	}
851
-
852
-	private static function registerAppRestrictionsHooks() {
853
-		/** @var \OC\Group\Manager $groupManager */
854
-		$groupManager = self::$server->query(\OCP\IGroupManager::class);
855
-		$groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
856
-			$appManager = self::$server->getAppManager();
857
-			$apps = $appManager->getEnabledAppsForGroup($group);
858
-			foreach ($apps as $appId) {
859
-				$restrictions = $appManager->getAppRestriction($appId);
860
-				if (empty($restrictions)) {
861
-					continue;
862
-				}
863
-				$key = array_search($group->getGID(), $restrictions);
864
-				unset($restrictions[$key]);
865
-				$restrictions = array_values($restrictions);
866
-				if (empty($restrictions)) {
867
-					$appManager->disableApp($appId);
868
-				} else {
869
-					$appManager->enableAppForGroups($appId, $restrictions);
870
-				}
871
-			}
872
-		});
873
-	}
874
-
875
-	private static function registerResourceCollectionHooks() {
876
-		\OC\Collaboration\Resources\Listener::register(\OC::$server->getEventDispatcher());
877
-	}
878
-
879
-	/**
880
-	 * register hooks for the filesystem
881
-	 */
882
-	public static function registerFilesystemHooks() {
883
-		// Check for blacklisted files
884
-		OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted');
885
-		OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted');
886
-	}
887
-
888
-	/**
889
-	 * register hooks for sharing
890
-	 */
891
-	public static function registerShareHooks(\OC\SystemConfig $systemConfig) {
892
-		if ($systemConfig->getValue('installed')) {
893
-			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
894
-			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
895
-
896
-			/** @var IEventDispatcher $dispatcher */
897
-			$dispatcher = \OC::$server->get(IEventDispatcher::class);
898
-			$dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
899
-		}
900
-	}
901
-
902
-	protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig) {
903
-		// The class loader takes an optional low-latency cache, which MUST be
904
-		// namespaced. The instanceid is used for namespacing, but might be
905
-		// unavailable at this point. Furthermore, it might not be possible to
906
-		// generate an instanceid via \OC_Util::getInstanceId() because the
907
-		// config file may not be writable. As such, we only register a class
908
-		// loader cache if instanceid is available without trying to create one.
909
-		$instanceId = $systemConfig->getValue('instanceid', null);
910
-		if ($instanceId) {
911
-			try {
912
-				$memcacheFactory = \OC::$server->getMemCacheFactory();
913
-				self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
914
-			} catch (\Exception $ex) {
915
-			}
916
-		}
917
-	}
918
-
919
-	/**
920
-	 * Handle the request
921
-	 */
922
-	public static function handleRequest() {
923
-		\OC::$server->getEventLogger()->start('handle_request', 'Handle request');
924
-		$systemConfig = \OC::$server->getSystemConfig();
925
-
926
-		// Check if Nextcloud is installed or in maintenance (update) mode
927
-		if (!$systemConfig->getValue('installed', false)) {
928
-			\OC::$server->getSession()->clear();
929
-			$setupHelper = new OC\Setup(
930
-				$systemConfig,
931
-				\OC::$server->get(\bantu\IniGetWrapper\IniGetWrapper::class),
932
-				\OC::$server->getL10N('lib'),
933
-				\OC::$server->query(\OCP\Defaults::class),
934
-				\OC::$server->get(\Psr\Log\LoggerInterface::class),
935
-				\OC::$server->getSecureRandom(),
936
-				\OC::$server->query(\OC\Installer::class)
937
-			);
938
-			$controller = new OC\Core\Controller\SetupController($setupHelper);
939
-			$controller->run($_POST);
940
-			exit();
941
-		}
942
-
943
-		$request = \OC::$server->getRequest();
944
-		$requestPath = $request->getRawPathInfo();
945
-		if ($requestPath === '/heartbeat') {
946
-			return;
947
-		}
948
-		if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
949
-			self::checkMaintenanceMode($systemConfig);
950
-
951
-			if (\OCP\Util::needUpgrade()) {
952
-				if (function_exists('opcache_reset')) {
953
-					opcache_reset();
954
-				}
955
-				if (!((bool) $systemConfig->getValue('maintenance', false))) {
956
-					self::printUpgradePage($systemConfig);
957
-					exit();
958
-				}
959
-			}
960
-		}
961
-
962
-		// emergency app disabling
963
-		if ($requestPath === '/disableapp'
964
-			&& $request->getMethod() === 'POST'
965
-			&& ((array)$request->getParam('appid')) !== ''
966
-		) {
967
-			\OC_JSON::callCheck();
968
-			\OC_JSON::checkAdminUser();
969
-			$appIds = (array)$request->getParam('appid');
970
-			foreach ($appIds as $appId) {
971
-				$appId = \OC_App::cleanAppId($appId);
972
-				\OC::$server->getAppManager()->disableApp($appId);
973
-			}
974
-			\OC_JSON::success();
975
-			exit();
976
-		}
977
-
978
-		// Always load authentication apps
979
-		OC_App::loadApps(['authentication']);
980
-
981
-		// Load minimum set of apps
982
-		if (!\OCP\Util::needUpgrade()
983
-			&& !((bool) $systemConfig->getValue('maintenance', false))) {
984
-			// For logged-in users: Load everything
985
-			if (\OC::$server->getUserSession()->isLoggedIn()) {
986
-				OC_App::loadApps();
987
-			} else {
988
-				// For guests: Load only filesystem and logging
989
-				OC_App::loadApps(['filesystem', 'logging']);
990
-				self::handleLogin($request);
991
-			}
992
-		}
993
-
994
-		if (!self::$CLI) {
995
-			try {
996
-				if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
997
-					OC_App::loadApps(['filesystem', 'logging']);
998
-					OC_App::loadApps();
999
-				}
1000
-				OC::$server->get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1001
-				return;
1002
-			} catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1003
-				//header('HTTP/1.0 404 Not Found');
1004
-			} catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1005
-				http_response_code(405);
1006
-				return;
1007
-			}
1008
-		}
1009
-
1010
-		// Handle WebDAV
1011
-		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1012
-			// not allowed any more to prevent people
1013
-			// mounting this root directly.
1014
-			// Users need to mount remote.php/webdav instead.
1015
-			http_response_code(405);
1016
-			return;
1017
-		}
1018
-
1019
-		// Someone is logged in
1020
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
1021
-			OC_App::loadApps();
1022
-			OC_User::setupBackends();
1023
-			OC_Util::setupFS();
1024
-			// FIXME
1025
-			// Redirect to default application
1026
-			OC_Util::redirectToDefaultPage();
1027
-		} else {
1028
-			// Not handled and not logged in
1029
-			header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
1030
-		}
1031
-	}
1032
-
1033
-	/**
1034
-	 * Check login: apache auth, auth token, basic auth
1035
-	 *
1036
-	 * @param OCP\IRequest $request
1037
-	 * @return boolean
1038
-	 */
1039
-	public static function handleLogin(OCP\IRequest $request) {
1040
-		$userSession = self::$server->getUserSession();
1041
-		if (OC_User::handleApacheAuth()) {
1042
-			return true;
1043
-		}
1044
-		if ($userSession->tryTokenLogin($request)) {
1045
-			return true;
1046
-		}
1047
-		if (isset($_COOKIE['nc_username'])
1048
-			&& isset($_COOKIE['nc_token'])
1049
-			&& isset($_COOKIE['nc_session_id'])
1050
-			&& $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1051
-			return true;
1052
-		}
1053
-		if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1054
-			return true;
1055
-		}
1056
-		return false;
1057
-	}
1058
-
1059
-	protected static function handleAuthHeaders() {
1060
-		//copy http auth headers for apache+php-fcgid work around
1061
-		if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1062
-			$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1063
-		}
1064
-
1065
-		// Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1066
-		$vars = [
1067
-			'HTTP_AUTHORIZATION', // apache+php-cgi work around
1068
-			'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1069
-		];
1070
-		foreach ($vars as $var) {
1071
-			if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1072
-				$credentials = explode(':', base64_decode($matches[1]), 2);
1073
-				if (count($credentials) === 2) {
1074
-					$_SERVER['PHP_AUTH_USER'] = $credentials[0];
1075
-					$_SERVER['PHP_AUTH_PW'] = $credentials[1];
1076
-					break;
1077
-				}
1078
-			}
1079
-		}
1080
-	}
81
+    /**
82
+     * Associative array for autoloading. classname => filename
83
+     */
84
+    public static $CLASSPATH = [];
85
+    /**
86
+     * The installation path for Nextcloud  on the server (e.g. /srv/http/nextcloud)
87
+     */
88
+    public static $SERVERROOT = '';
89
+    /**
90
+     * the current request path relative to the Nextcloud root (e.g. files/index.php)
91
+     */
92
+    private static $SUBURI = '';
93
+    /**
94
+     * the Nextcloud root path for http requests (e.g. nextcloud/)
95
+     */
96
+    public static $WEBROOT = '';
97
+    /**
98
+     * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
99
+     * web path in 'url'
100
+     */
101
+    public static $APPSROOTS = [];
102
+
103
+    /**
104
+     * @var string
105
+     */
106
+    public static $configDir;
107
+
108
+    /**
109
+     * requested app
110
+     */
111
+    public static $REQUESTEDAPP = '';
112
+
113
+    /**
114
+     * check if Nextcloud runs in cli mode
115
+     */
116
+    public static $CLI = false;
117
+
118
+    /**
119
+     * @var \OC\Autoloader $loader
120
+     */
121
+    public static $loader = null;
122
+
123
+    /** @var \Composer\Autoload\ClassLoader $composerAutoloader */
124
+    public static $composerAutoloader = null;
125
+
126
+    /**
127
+     * @var \OC\Server
128
+     */
129
+    public static $server = null;
130
+
131
+    /**
132
+     * @var \OC\Config
133
+     */
134
+    private static $config = null;
135
+
136
+    /**
137
+     * @throws \RuntimeException when the 3rdparty directory is missing or
138
+     * the app path list is empty or contains an invalid path
139
+     */
140
+    public static function initPaths() {
141
+        if (defined('PHPUNIT_CONFIG_DIR')) {
142
+            self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
143
+        } elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
144
+            self::$configDir = OC::$SERVERROOT . '/tests/config/';
145
+        } elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
146
+            self::$configDir = rtrim($dir, '/') . '/';
147
+        } else {
148
+            self::$configDir = OC::$SERVERROOT . '/config/';
149
+        }
150
+        self::$config = new \OC\Config(self::$configDir);
151
+
152
+        OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
153
+        /**
154
+         * FIXME: The following lines are required because we can't yet instantiate
155
+         *        \OC::$server->getRequest() since \OC::$server does not yet exist.
156
+         */
157
+        $params = [
158
+            'server' => [
159
+                'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
160
+                'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
161
+            ],
162
+        ];
163
+        $fakeRequest = new \OC\AppFramework\Http\Request($params, new \OC\Security\SecureRandom(), new \OC\AllConfig(new \OC\SystemConfig(self::$config)));
164
+        $scriptName = $fakeRequest->getScriptName();
165
+        if (substr($scriptName, -1) == '/') {
166
+            $scriptName .= 'index.php';
167
+            //make sure suburi follows the same rules as scriptName
168
+            if (substr(OC::$SUBURI, -9) != 'index.php') {
169
+                if (substr(OC::$SUBURI, -1) != '/') {
170
+                    OC::$SUBURI = OC::$SUBURI . '/';
171
+                }
172
+                OC::$SUBURI = OC::$SUBURI . 'index.php';
173
+            }
174
+        }
175
+
176
+
177
+        if (OC::$CLI) {
178
+            OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
179
+        } else {
180
+            if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) {
181
+                OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI));
182
+
183
+                if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') {
184
+                    OC::$WEBROOT = '/' . OC::$WEBROOT;
185
+                }
186
+            } else {
187
+                // The scriptName is not ending with OC::$SUBURI
188
+                // This most likely means that we are calling from CLI.
189
+                // However some cron jobs still need to generate
190
+                // a web URL, so we use overwritewebroot as a fallback.
191
+                OC::$WEBROOT = self::$config->getValue('overwritewebroot', '');
192
+            }
193
+
194
+            // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing
195
+            // slash which is required by URL generation.
196
+            if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT &&
197
+                    substr($_SERVER['REQUEST_URI'], -1) !== '/') {
198
+                header('Location: '.\OC::$WEBROOT.'/');
199
+                exit();
200
+            }
201
+        }
202
+
203
+        // search the apps folder
204
+        $config_paths = self::$config->getValue('apps_paths', []);
205
+        if (!empty($config_paths)) {
206
+            foreach ($config_paths as $paths) {
207
+                if (isset($paths['url']) && isset($paths['path'])) {
208
+                    $paths['url'] = rtrim($paths['url'], '/');
209
+                    $paths['path'] = rtrim($paths['path'], '/');
210
+                    OC::$APPSROOTS[] = $paths;
211
+                }
212
+            }
213
+        } elseif (file_exists(OC::$SERVERROOT . '/apps')) {
214
+            OC::$APPSROOTS[] = ['path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true];
215
+        }
216
+
217
+        if (empty(OC::$APPSROOTS)) {
218
+            throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder'
219
+                . '. You can also configure the location in the config.php file.');
220
+        }
221
+        $paths = [];
222
+        foreach (OC::$APPSROOTS as $path) {
223
+            $paths[] = $path['path'];
224
+            if (!is_dir($path['path'])) {
225
+                throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the'
226
+                    . ' Nextcloud folder. You can also configure the location in the config.php file.', $path['path']));
227
+            }
228
+        }
229
+
230
+        // set the right include path
231
+        set_include_path(
232
+            implode(PATH_SEPARATOR, $paths)
233
+        );
234
+    }
235
+
236
+    public static function checkConfig() {
237
+        $l = \OC::$server->getL10N('lib');
238
+
239
+        // Create config if it does not already exist
240
+        $configFilePath = self::$configDir .'/config.php';
241
+        if (!file_exists($configFilePath)) {
242
+            @touch($configFilePath);
243
+        }
244
+
245
+        // Check if config is writable
246
+        $configFileWritable = is_writable($configFilePath);
247
+        if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled()
248
+            || !$configFileWritable && \OCP\Util::needUpgrade()) {
249
+            $urlGenerator = \OC::$server->getURLGenerator();
250
+
251
+            if (self::$CLI) {
252
+                echo $l->t('Cannot write into "config" directory!')."\n";
253
+                echo $l->t('This can usually be fixed by giving the webserver write access to the config directory.')."\n";
254
+                echo "\n";
255
+                echo $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.')."\n";
256
+                echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ])."\n";
257
+                exit;
258
+            } else {
259
+                OC_Template::printErrorPage(
260
+                    $l->t('Cannot write into "config" directory!'),
261
+                    $l->t('This can usually be fixed by giving the webserver write access to the config directory.') . ' '
262
+                    . $l->t('But, if you prefer to keep config.php file read only, set the option "config_is_read_only" to true in it.') . ' '
263
+                    . $l->t('See %s', [ $urlGenerator->linkToDocs('admin-config') ]),
264
+                    503
265
+                );
266
+            }
267
+        }
268
+    }
269
+
270
+    public static function checkInstalled(\OC\SystemConfig $systemConfig) {
271
+        if (defined('OC_CONSOLE')) {
272
+            return;
273
+        }
274
+        // Redirect to installer if not installed
275
+        if (!$systemConfig->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') {
276
+            if (OC::$CLI) {
277
+                throw new Exception('Not installed');
278
+            } else {
279
+                $url = OC::$WEBROOT . '/index.php';
280
+                header('Location: ' . $url);
281
+            }
282
+            exit();
283
+        }
284
+    }
285
+
286
+    public static function checkMaintenanceMode(\OC\SystemConfig $systemConfig) {
287
+        // Allow ajax update script to execute without being stopped
288
+        if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
289
+            // send http status 503
290
+            http_response_code(503);
291
+            header('Retry-After: 120');
292
+
293
+            // render error page
294
+            $template = new OC_Template('', 'update.user', 'guest');
295
+            OC_Util::addScript('dist/maintenance');
296
+            OC_Util::addStyle('core', 'guest');
297
+            $template->printPage();
298
+            die();
299
+        }
300
+    }
301
+
302
+    /**
303
+     * Prints the upgrade page
304
+     *
305
+     * @param \OC\SystemConfig $systemConfig
306
+     */
307
+    private static function printUpgradePage(\OC\SystemConfig $systemConfig) {
308
+        $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false);
309
+        $tooBig = false;
310
+        if (!$disableWebUpdater) {
311
+            $apps = \OC::$server->getAppManager();
312
+            if ($apps->isInstalled('user_ldap')) {
313
+                $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
314
+
315
+                $result = $qb->select($qb->func()->count('*', 'user_count'))
316
+                    ->from('ldap_user_mapping')
317
+                    ->execute();
318
+                $row = $result->fetch();
319
+                $result->closeCursor();
320
+
321
+                $tooBig = ($row['user_count'] > 50);
322
+            }
323
+            if (!$tooBig && $apps->isInstalled('user_saml')) {
324
+                $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
325
+
326
+                $result = $qb->select($qb->func()->count('*', 'user_count'))
327
+                    ->from('user_saml_users')
328
+                    ->execute();
329
+                $row = $result->fetch();
330
+                $result->closeCursor();
331
+
332
+                $tooBig = ($row['user_count'] > 50);
333
+            }
334
+            if (!$tooBig) {
335
+                // count users
336
+                $stats = \OC::$server->getUserManager()->countUsers();
337
+                $totalUsers = array_sum($stats);
338
+                $tooBig = ($totalUsers > 50);
339
+            }
340
+        }
341
+        $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) &&
342
+            $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis';
343
+
344
+        if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) {
345
+            // send http status 503
346
+            http_response_code(503);
347
+            header('Retry-After: 120');
348
+
349
+            // render error page
350
+            $template = new OC_Template('', 'update.use-cli', 'guest');
351
+            $template->assign('productName', 'nextcloud'); // for now
352
+            $template->assign('version', OC_Util::getVersionString());
353
+            $template->assign('tooBig', $tooBig);
354
+
355
+            $template->printPage();
356
+            die();
357
+        }
358
+
359
+        // check whether this is a core update or apps update
360
+        $installedVersion = $systemConfig->getValue('version', '0.0.0');
361
+        $currentVersion = implode('.', \OCP\Util::getVersion());
362
+
363
+        // if not a core upgrade, then it's apps upgrade
364
+        $isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '=');
365
+
366
+        $oldTheme = $systemConfig->getValue('theme');
367
+        $systemConfig->setValue('theme', '');
368
+        OC_Util::addScript('update');
369
+
370
+        /** @var \OC\App\AppManager $appManager */
371
+        $appManager = \OC::$server->getAppManager();
372
+
373
+        $tmpl = new OC_Template('', 'update.admin', 'guest');
374
+        $tmpl->assign('version', OC_Util::getVersionString());
375
+        $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade);
376
+
377
+        // get third party apps
378
+        $ocVersion = \OCP\Util::getVersion();
379
+        $ocVersion = implode('.', $ocVersion);
380
+        $incompatibleApps = $appManager->getIncompatibleApps($ocVersion);
381
+        $incompatibleShippedApps = [];
382
+        foreach ($incompatibleApps as $appInfo) {
383
+            if ($appManager->isShipped($appInfo['id'])) {
384
+                $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')';
385
+            }
386
+        }
387
+
388
+        if (!empty($incompatibleShippedApps)) {
389
+            $l = \OC::$server->getL10N('core');
390
+            $hint = $l->t('The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]);
391
+            throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint);
392
+        }
393
+
394
+        $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion));
395
+        $tmpl->assign('incompatibleAppsList', $incompatibleApps);
396
+        try {
397
+            $defaults = new \OC_Defaults();
398
+            $tmpl->assign('productName', $defaults->getName());
399
+        } catch (Throwable $error) {
400
+            $tmpl->assign('productName', 'Nextcloud');
401
+        }
402
+        $tmpl->assign('oldTheme', $oldTheme);
403
+        $tmpl->printPage();
404
+    }
405
+
406
+    public static function initSession() {
407
+        if (self::$server->getRequest()->getServerProtocol() === 'https') {
408
+            ini_set('session.cookie_secure', 'true');
409
+        }
410
+
411
+        // prevents javascript from accessing php session cookies
412
+        ini_set('session.cookie_httponly', 'true');
413
+
414
+        // set the cookie path to the Nextcloud directory
415
+        $cookie_path = OC::$WEBROOT ? : '/';
416
+        ini_set('session.cookie_path', $cookie_path);
417
+
418
+        // Let the session name be changed in the initSession Hook
419
+        $sessionName = OC_Util::getInstanceId();
420
+
421
+        try {
422
+            // set the session name to the instance id - which is unique
423
+            $session = new \OC\Session\Internal($sessionName);
424
+
425
+            $cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
426
+            $session = $cryptoWrapper->wrapSession($session);
427
+            self::$server->setSession($session);
428
+
429
+            // if session can't be started break with http 500 error
430
+        } catch (Exception $e) {
431
+            \OC::$server->getLogger()->logException($e, ['app' => 'base']);
432
+            //show the user a detailed error page
433
+            OC_Template::printExceptionErrorPage($e, 500);
434
+            die();
435
+        }
436
+
437
+        $sessionLifeTime = self::getSessionLifeTime();
438
+
439
+        // session timeout
440
+        if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
441
+            if (isset($_COOKIE[session_name()])) {
442
+                setcookie(session_name(), '', -1, self::$WEBROOT ? : '/');
443
+            }
444
+            \OC::$server->getUserSession()->logout();
445
+        }
446
+
447
+        $session->set('LAST_ACTIVITY', time());
448
+    }
449
+
450
+    /**
451
+     * @return string
452
+     */
453
+    private static function getSessionLifeTime() {
454
+        return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24);
455
+    }
456
+
457
+    /**
458
+     * Try to set some values to the required Nextcloud default
459
+     */
460
+    public static function setRequiredIniValues() {
461
+        @ini_set('default_charset', 'UTF-8');
462
+        @ini_set('gd.jpeg_ignore_warning', '1');
463
+    }
464
+
465
+    /**
466
+     * Send the same site cookies
467
+     */
468
+    private static function sendSameSiteCookies() {
469
+        $cookieParams = session_get_cookie_params();
470
+        $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
471
+        $policies = [
472
+            'lax',
473
+            'strict',
474
+        ];
475
+
476
+        // Append __Host to the cookie if it meets the requirements
477
+        $cookiePrefix = '';
478
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
479
+            $cookiePrefix = '__Host-';
480
+        }
481
+
482
+        foreach ($policies as $policy) {
483
+            header(
484
+                sprintf(
485
+                    'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
486
+                    $cookiePrefix,
487
+                    $policy,
488
+                    $cookieParams['path'],
489
+                    $policy
490
+                ),
491
+                false
492
+            );
493
+        }
494
+    }
495
+
496
+    /**
497
+     * Same Site cookie to further mitigate CSRF attacks. This cookie has to
498
+     * be set in every request if cookies are sent to add a second level of
499
+     * defense against CSRF.
500
+     *
501
+     * If the cookie is not sent this will set the cookie and reload the page.
502
+     * We use an additional cookie since we want to protect logout CSRF and
503
+     * also we can't directly interfere with PHP's session mechanism.
504
+     */
505
+    private static function performSameSiteCookieProtection(\OCP\IConfig $config) {
506
+        $request = \OC::$server->getRequest();
507
+
508
+        // Some user agents are notorious and don't really properly follow HTTP
509
+        // specifications. For those, have an automated opt-out. Since the protection
510
+        // for remote.php is applied in base.php as starting point we need to opt out
511
+        // here.
512
+        $incompatibleUserAgents = $config->getSystemValue('csrf.optout');
513
+
514
+        // Fallback, if csrf.optout is unset
515
+        if (!is_array($incompatibleUserAgents)) {
516
+            $incompatibleUserAgents = [
517
+                // OS X Finder
518
+                '/^WebDAVFS/',
519
+                // Windows webdav drive
520
+                '/^Microsoft-WebDAV-MiniRedir/',
521
+            ];
522
+        }
523
+
524
+        if ($request->isUserAgent($incompatibleUserAgents)) {
525
+            return;
526
+        }
527
+
528
+        if (count($_COOKIE) > 0) {
529
+            $requestUri = $request->getScriptName();
530
+            $processingScript = explode('/', $requestUri);
531
+            $processingScript = $processingScript[count($processingScript) - 1];
532
+
533
+            // index.php routes are handled in the middleware
534
+            if ($processingScript === 'index.php') {
535
+                return;
536
+            }
537
+
538
+            // All other endpoints require the lax and the strict cookie
539
+            if (!$request->passesStrictCookieCheck()) {
540
+                self::sendSameSiteCookies();
541
+                // Debug mode gets access to the resources without strict cookie
542
+                // due to the fact that the SabreDAV browser also lives there.
543
+                if (!$config->getSystemValue('debug', false)) {
544
+                    http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE);
545
+                    exit();
546
+                }
547
+            }
548
+        } elseif (!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) {
549
+            self::sendSameSiteCookies();
550
+        }
551
+    }
552
+
553
+    public static function init() {
554
+        // calculate the root directories
555
+        OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
556
+
557
+        // register autoloader
558
+        $loaderStart = microtime(true);
559
+        require_once __DIR__ . '/autoloader.php';
560
+        self::$loader = new \OC\Autoloader([
561
+            OC::$SERVERROOT . '/lib/private/legacy',
562
+        ]);
563
+        if (defined('PHPUNIT_RUN')) {
564
+            self::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
565
+        }
566
+        spl_autoload_register([self::$loader, 'load']);
567
+        $loaderEnd = microtime(true);
568
+
569
+        self::$CLI = (php_sapi_name() == 'cli');
570
+
571
+        // Add default composer PSR-4 autoloader
572
+        self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php';
573
+
574
+        try {
575
+            self::initPaths();
576
+            // setup 3rdparty autoloader
577
+            $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php';
578
+            if (!file_exists($vendorAutoLoad)) {
579
+                throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".');
580
+            }
581
+            require_once $vendorAutoLoad;
582
+        } catch (\RuntimeException $e) {
583
+            if (!self::$CLI) {
584
+                http_response_code(503);
585
+            }
586
+            // we can't use the template error page here, because this needs the
587
+            // DI container which isn't available yet
588
+            print($e->getMessage());
589
+            exit();
590
+        }
591
+
592
+        // setup the basic server
593
+        self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
594
+        self::$server->boot();
595
+        $eventLogger = \OC::$server->getEventLogger();
596
+        $eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
597
+        $eventLogger->start('boot', 'Initialize');
598
+
599
+        // Override php.ini and log everything if we're troubleshooting
600
+        if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
601
+            error_reporting(E_ALL);
602
+        }
603
+
604
+        // Don't display errors and log them
605
+        @ini_set('display_errors', '0');
606
+        @ini_set('log_errors', '1');
607
+
608
+        if (!date_default_timezone_set('UTC')) {
609
+            throw new \RuntimeException('Could not set timezone to UTC');
610
+        }
611
+
612
+        //try to configure php to enable big file uploads.
613
+        //this doesn´t work always depending on the webserver and php configuration.
614
+        //Let´s try to overwrite some defaults anyway
615
+
616
+        //try to set the maximum execution time to 60min
617
+        if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
618
+            @set_time_limit(3600);
619
+        }
620
+        @ini_set('max_execution_time', '3600');
621
+        @ini_set('max_input_time', '3600');
622
+
623
+        self::setRequiredIniValues();
624
+        self::handleAuthHeaders();
625
+        $systemConfig = \OC::$server->get(\OC\SystemConfig::class);
626
+        self::registerAutoloaderCache($systemConfig);
627
+
628
+        // initialize intl fallback if necessary
629
+        OC_Util::isSetLocaleWorking();
630
+
631
+        $config = \OC::$server->get(\OCP\IConfig::class);
632
+        if (!defined('PHPUNIT_RUN')) {
633
+            OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger());
634
+            $debug = $config->getSystemValue('debug', false);
635
+            OC\Log\ErrorHandler::register($debug);
636
+        }
637
+
638
+        /** @var \OC\AppFramework\Bootstrap\Coordinator $bootstrapCoordinator */
639
+        $bootstrapCoordinator = \OC::$server->query(\OC\AppFramework\Bootstrap\Coordinator::class);
640
+        $bootstrapCoordinator->runInitialRegistration();
641
+
642
+        $eventLogger->start('init_session', 'Initialize session');
643
+        OC_App::loadApps(['session']);
644
+        if (!self::$CLI) {
645
+            self::initSession();
646
+        }
647
+        $eventLogger->end('init_session');
648
+        self::checkConfig();
649
+        self::checkInstalled($systemConfig);
650
+
651
+        OC_Response::addSecurityHeaders();
652
+
653
+        self::performSameSiteCookieProtection($config);
654
+
655
+        if (!defined('OC_CONSOLE')) {
656
+            $errors = OC_Util::checkServer($systemConfig);
657
+            if (count($errors) > 0) {
658
+                if (!self::$CLI) {
659
+                    http_response_code(503);
660
+                    OC_Util::addStyle('guest');
661
+                    try {
662
+                        OC_Template::printGuestPage('', 'error', ['errors' => $errors]);
663
+                        exit;
664
+                    } catch (\Exception $e) {
665
+                        // In case any error happens when showing the error page, we simply fall back to posting the text.
666
+                        // This might be the case when e.g. the data directory is broken and we can not load/write SCSS to/from it.
667
+                    }
668
+                }
669
+
670
+                // Convert l10n string into regular string for usage in database
671
+                $staticErrors = [];
672
+                foreach ($errors as $error) {
673
+                    echo $error['error'] . "\n";
674
+                    echo $error['hint'] . "\n\n";
675
+                    $staticErrors[] = [
676
+                        'error' => (string)$error['error'],
677
+                        'hint' => (string)$error['hint'],
678
+                    ];
679
+                }
680
+
681
+                try {
682
+                    $config->setAppValue('core', 'cronErrors', json_encode($staticErrors));
683
+                } catch (\Exception $e) {
684
+                    echo('Writing to database failed');
685
+                }
686
+                exit(1);
687
+            } elseif (self::$CLI && $config->getSystemValue('installed', false)) {
688
+                $config->deleteAppValue('core', 'cronErrors');
689
+            }
690
+        }
691
+        //try to set the session lifetime
692
+        $sessionLifeTime = self::getSessionLifeTime();
693
+        @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
694
+
695
+        // User and Groups
696
+        if (!$systemConfig->getValue("installed", false)) {
697
+            self::$server->getSession()->set('user_id', '');
698
+        }
699
+
700
+        OC_User::useBackend(new \OC\User\Database());
701
+        \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database());
702
+
703
+        // Subscribe to the hook
704
+        \OCP\Util::connectHook(
705
+            '\OCA\Files_Sharing\API\Server2Server',
706
+            'preLoginNameUsedAsUserName',
707
+            '\OC\User\Database',
708
+            'preLoginNameUsedAsUserName'
709
+        );
710
+
711
+        //setup extra user backends
712
+        if (!\OCP\Util::needUpgrade()) {
713
+            OC_User::setupBackends();
714
+        } else {
715
+            // Run upgrades in incognito mode
716
+            OC_User::setIncognitoMode(true);
717
+        }
718
+
719
+        self::registerCleanupHooks($systemConfig);
720
+        self::registerFilesystemHooks();
721
+        self::registerShareHooks($systemConfig);
722
+        self::registerEncryptionWrapperAndHooks();
723
+        self::registerAccountHooks();
724
+        self::registerResourceCollectionHooks();
725
+        self::registerAppRestrictionsHooks();
726
+
727
+        // Make sure that the application class is not loaded before the database is setup
728
+        if ($systemConfig->getValue("installed", false)) {
729
+            OC_App::loadApp('settings');
730
+        }
731
+
732
+        //make sure temporary files are cleaned up
733
+        $tmpManager = \OC::$server->getTempManager();
734
+        register_shutdown_function([$tmpManager, 'clean']);
735
+        $lockProvider = \OC::$server->getLockingProvider();
736
+        register_shutdown_function([$lockProvider, 'releaseAll']);
737
+
738
+        // Check whether the sample configuration has been copied
739
+        if ($systemConfig->getValue('copied_sample_config', false)) {
740
+            $l = \OC::$server->getL10N('lib');
741
+            OC_Template::printErrorPage(
742
+                $l->t('Sample configuration detected'),
743
+                $l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php'),
744
+                503
745
+            );
746
+            return;
747
+        }
748
+
749
+        $request = \OC::$server->getRequest();
750
+        $host = $request->getInsecureServerHost();
751
+        /**
752
+         * if the host passed in headers isn't trusted
753
+         * FIXME: Should not be in here at all :see_no_evil:
754
+         */
755
+        if (!OC::$CLI
756
+            && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host)
757
+            && $config->getSystemValue('installed', false)
758
+        ) {
759
+            // Allow access to CSS resources
760
+            $isScssRequest = false;
761
+            if (strpos($request->getPathInfo(), '/css/') === 0) {
762
+                $isScssRequest = true;
763
+            }
764
+
765
+            if (substr($request->getRequestUri(), -11) === '/status.php') {
766
+                http_response_code(400);
767
+                header('Content-Type: application/json');
768
+                echo '{"error": "Trusted domain error.", "code": 15}';
769
+                exit();
770
+            }
771
+
772
+            if (!$isScssRequest) {
773
+                http_response_code(400);
774
+
775
+                \OC::$server->getLogger()->info(
776
+                    'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.',
777
+                    [
778
+                        'app' => 'core',
779
+                        'remoteAddress' => $request->getRemoteAddress(),
780
+                        'host' => $host,
781
+                    ]
782
+                );
783
+
784
+                $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest');
785
+                $tmpl->assign('docUrl', \OC::$server->getURLGenerator()->linkToDocs('admin-trusted-domains'));
786
+                $tmpl->printPage();
787
+
788
+                exit();
789
+            }
790
+        }
791
+        $eventLogger->end('boot');
792
+    }
793
+
794
+    /**
795
+     * register hooks for the cleanup of cache and bruteforce protection
796
+     */
797
+    public static function registerCleanupHooks(\OC\SystemConfig $systemConfig) {
798
+        //don't try to do this before we are properly setup
799
+        if ($systemConfig->getValue('installed', false) && !\OCP\Util::needUpgrade()) {
800
+
801
+            // NOTE: This will be replaced to use OCP
802
+            $userSession = self::$server->getUserSession();
803
+            $userSession->listen('\OC\User', 'postLogin', function () use ($userSession) {
804
+                if (!defined('PHPUNIT_RUN') && $userSession->isLoggedIn()) {
805
+                    // reset brute force delay for this IP address and username
806
+                    $uid = \OC::$server->getUserSession()->getUser()->getUID();
807
+                    $request = \OC::$server->getRequest();
808
+                    $throttler = \OC::$server->getBruteForceThrottler();
809
+                    $throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]);
810
+                }
811
+
812
+                try {
813
+                    $cache = new \OC\Cache\File();
814
+                    $cache->gc();
815
+                } catch (\OC\ServerNotAvailableException $e) {
816
+                    // not a GC exception, pass it on
817
+                    throw $e;
818
+                } catch (\OC\ForbiddenException $e) {
819
+                    // filesystem blocked for this request, ignore
820
+                } catch (\Exception $e) {
821
+                    // a GC exception should not prevent users from using OC,
822
+                    // so log the exception
823
+                    \OC::$server->getLogger()->logException($e, [
824
+                        'message' => 'Exception when running cache gc.',
825
+                        'level' => ILogger::WARN,
826
+                        'app' => 'core',
827
+                    ]);
828
+                }
829
+            });
830
+        }
831
+    }
832
+
833
+    private static function registerEncryptionWrapperAndHooks() {
834
+        $manager = self::$server->getEncryptionManager();
835
+        \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage');
836
+
837
+        $enabled = $manager->isEnabled();
838
+        if ($enabled) {
839
+            \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared');
840
+            \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared');
841
+            \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename');
842
+            \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore');
843
+        }
844
+    }
845
+
846
+    private static function registerAccountHooks() {
847
+        /** @var IEventDispatcher $dispatcher */
848
+        $dispatcher = \OC::$server->get(IEventDispatcher::class);
849
+        $dispatcher->addServiceListener(UserChangedEvent::class, \OC\Accounts\Hooks::class);
850
+    }
851
+
852
+    private static function registerAppRestrictionsHooks() {
853
+        /** @var \OC\Group\Manager $groupManager */
854
+        $groupManager = self::$server->query(\OCP\IGroupManager::class);
855
+        $groupManager->listen('\OC\Group', 'postDelete', function (\OCP\IGroup $group) {
856
+            $appManager = self::$server->getAppManager();
857
+            $apps = $appManager->getEnabledAppsForGroup($group);
858
+            foreach ($apps as $appId) {
859
+                $restrictions = $appManager->getAppRestriction($appId);
860
+                if (empty($restrictions)) {
861
+                    continue;
862
+                }
863
+                $key = array_search($group->getGID(), $restrictions);
864
+                unset($restrictions[$key]);
865
+                $restrictions = array_values($restrictions);
866
+                if (empty($restrictions)) {
867
+                    $appManager->disableApp($appId);
868
+                } else {
869
+                    $appManager->enableAppForGroups($appId, $restrictions);
870
+                }
871
+            }
872
+        });
873
+    }
874
+
875
+    private static function registerResourceCollectionHooks() {
876
+        \OC\Collaboration\Resources\Listener::register(\OC::$server->getEventDispatcher());
877
+    }
878
+
879
+    /**
880
+     * register hooks for the filesystem
881
+     */
882
+    public static function registerFilesystemHooks() {
883
+        // Check for blacklisted files
884
+        OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted');
885
+        OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted');
886
+    }
887
+
888
+    /**
889
+     * register hooks for sharing
890
+     */
891
+    public static function registerShareHooks(\OC\SystemConfig $systemConfig) {
892
+        if ($systemConfig->getValue('installed')) {
893
+            OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
894
+            OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
895
+
896
+            /** @var IEventDispatcher $dispatcher */
897
+            $dispatcher = \OC::$server->get(IEventDispatcher::class);
898
+            $dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
899
+        }
900
+    }
901
+
902
+    protected static function registerAutoloaderCache(\OC\SystemConfig $systemConfig) {
903
+        // The class loader takes an optional low-latency cache, which MUST be
904
+        // namespaced. The instanceid is used for namespacing, but might be
905
+        // unavailable at this point. Furthermore, it might not be possible to
906
+        // generate an instanceid via \OC_Util::getInstanceId() because the
907
+        // config file may not be writable. As such, we only register a class
908
+        // loader cache if instanceid is available without trying to create one.
909
+        $instanceId = $systemConfig->getValue('instanceid', null);
910
+        if ($instanceId) {
911
+            try {
912
+                $memcacheFactory = \OC::$server->getMemCacheFactory();
913
+                self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader'));
914
+            } catch (\Exception $ex) {
915
+            }
916
+        }
917
+    }
918
+
919
+    /**
920
+     * Handle the request
921
+     */
922
+    public static function handleRequest() {
923
+        \OC::$server->getEventLogger()->start('handle_request', 'Handle request');
924
+        $systemConfig = \OC::$server->getSystemConfig();
925
+
926
+        // Check if Nextcloud is installed or in maintenance (update) mode
927
+        if (!$systemConfig->getValue('installed', false)) {
928
+            \OC::$server->getSession()->clear();
929
+            $setupHelper = new OC\Setup(
930
+                $systemConfig,
931
+                \OC::$server->get(\bantu\IniGetWrapper\IniGetWrapper::class),
932
+                \OC::$server->getL10N('lib'),
933
+                \OC::$server->query(\OCP\Defaults::class),
934
+                \OC::$server->get(\Psr\Log\LoggerInterface::class),
935
+                \OC::$server->getSecureRandom(),
936
+                \OC::$server->query(\OC\Installer::class)
937
+            );
938
+            $controller = new OC\Core\Controller\SetupController($setupHelper);
939
+            $controller->run($_POST);
940
+            exit();
941
+        }
942
+
943
+        $request = \OC::$server->getRequest();
944
+        $requestPath = $request->getRawPathInfo();
945
+        if ($requestPath === '/heartbeat') {
946
+            return;
947
+        }
948
+        if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade
949
+            self::checkMaintenanceMode($systemConfig);
950
+
951
+            if (\OCP\Util::needUpgrade()) {
952
+                if (function_exists('opcache_reset')) {
953
+                    opcache_reset();
954
+                }
955
+                if (!((bool) $systemConfig->getValue('maintenance', false))) {
956
+                    self::printUpgradePage($systemConfig);
957
+                    exit();
958
+                }
959
+            }
960
+        }
961
+
962
+        // emergency app disabling
963
+        if ($requestPath === '/disableapp'
964
+            && $request->getMethod() === 'POST'
965
+            && ((array)$request->getParam('appid')) !== ''
966
+        ) {
967
+            \OC_JSON::callCheck();
968
+            \OC_JSON::checkAdminUser();
969
+            $appIds = (array)$request->getParam('appid');
970
+            foreach ($appIds as $appId) {
971
+                $appId = \OC_App::cleanAppId($appId);
972
+                \OC::$server->getAppManager()->disableApp($appId);
973
+            }
974
+            \OC_JSON::success();
975
+            exit();
976
+        }
977
+
978
+        // Always load authentication apps
979
+        OC_App::loadApps(['authentication']);
980
+
981
+        // Load minimum set of apps
982
+        if (!\OCP\Util::needUpgrade()
983
+            && !((bool) $systemConfig->getValue('maintenance', false))) {
984
+            // For logged-in users: Load everything
985
+            if (\OC::$server->getUserSession()->isLoggedIn()) {
986
+                OC_App::loadApps();
987
+            } else {
988
+                // For guests: Load only filesystem and logging
989
+                OC_App::loadApps(['filesystem', 'logging']);
990
+                self::handleLogin($request);
991
+            }
992
+        }
993
+
994
+        if (!self::$CLI) {
995
+            try {
996
+                if (!((bool) $systemConfig->getValue('maintenance', false)) && !\OCP\Util::needUpgrade()) {
997
+                    OC_App::loadApps(['filesystem', 'logging']);
998
+                    OC_App::loadApps();
999
+                }
1000
+                OC::$server->get(\OC\Route\Router::class)->match($request->getRawPathInfo());
1001
+                return;
1002
+            } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
1003
+                //header('HTTP/1.0 404 Not Found');
1004
+            } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) {
1005
+                http_response_code(405);
1006
+                return;
1007
+            }
1008
+        }
1009
+
1010
+        // Handle WebDAV
1011
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
1012
+            // not allowed any more to prevent people
1013
+            // mounting this root directly.
1014
+            // Users need to mount remote.php/webdav instead.
1015
+            http_response_code(405);
1016
+            return;
1017
+        }
1018
+
1019
+        // Someone is logged in
1020
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
1021
+            OC_App::loadApps();
1022
+            OC_User::setupBackends();
1023
+            OC_Util::setupFS();
1024
+            // FIXME
1025
+            // Redirect to default application
1026
+            OC_Util::redirectToDefaultPage();
1027
+        } else {
1028
+            // Not handled and not logged in
1029
+            header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
1030
+        }
1031
+    }
1032
+
1033
+    /**
1034
+     * Check login: apache auth, auth token, basic auth
1035
+     *
1036
+     * @param OCP\IRequest $request
1037
+     * @return boolean
1038
+     */
1039
+    public static function handleLogin(OCP\IRequest $request) {
1040
+        $userSession = self::$server->getUserSession();
1041
+        if (OC_User::handleApacheAuth()) {
1042
+            return true;
1043
+        }
1044
+        if ($userSession->tryTokenLogin($request)) {
1045
+            return true;
1046
+        }
1047
+        if (isset($_COOKIE['nc_username'])
1048
+            && isset($_COOKIE['nc_token'])
1049
+            && isset($_COOKIE['nc_session_id'])
1050
+            && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) {
1051
+            return true;
1052
+        }
1053
+        if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) {
1054
+            return true;
1055
+        }
1056
+        return false;
1057
+    }
1058
+
1059
+    protected static function handleAuthHeaders() {
1060
+        //copy http auth headers for apache+php-fcgid work around
1061
+        if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
1062
+            $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION'];
1063
+        }
1064
+
1065
+        // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary.
1066
+        $vars = [
1067
+            'HTTP_AUTHORIZATION', // apache+php-cgi work around
1068
+            'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative
1069
+        ];
1070
+        foreach ($vars as $var) {
1071
+            if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) {
1072
+                $credentials = explode(':', base64_decode($matches[1]), 2);
1073
+                if (count($credentials) === 2) {
1074
+                    $_SERVER['PHP_AUTH_USER'] = $credentials[0];
1075
+                    $_SERVER['PHP_AUTH_PW'] = $credentials[1];
1076
+                    break;
1077
+                }
1078
+            }
1079
+        }
1080
+    }
1081 1081
 }
1082 1082
 
1083 1083
 OC::init();
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 2 patches
Indentation   +1210 added lines, -1210 removed lines patch added patch discarded remove patch
@@ -76,1214 +76,1214 @@
 block discarded – undo
76 76
 
77 77
 class UsersController extends AUserData {
78 78
 
79
-	/** @var IURLGenerator */
80
-	protected $urlGenerator;
81
-	/** @var LoggerInterface */
82
-	private $logger;
83
-	/** @var IFactory */
84
-	protected $l10nFactory;
85
-	/** @var NewUserMailHelper */
86
-	private $newUserMailHelper;
87
-	/** @var ISecureRandom */
88
-	private $secureRandom;
89
-	/** @var RemoteWipe */
90
-	private $remoteWipe;
91
-	/** @var KnownUserService */
92
-	private $knownUserService;
93
-	/** @var IEventDispatcher */
94
-	private $eventDispatcher;
95
-
96
-	public function __construct(string $appName,
97
-								IRequest $request,
98
-								IUserManager $userManager,
99
-								IConfig $config,
100
-								IGroupManager $groupManager,
101
-								IUserSession $userSession,
102
-								IAccountManager $accountManager,
103
-								IURLGenerator $urlGenerator,
104
-								LoggerInterface $logger,
105
-								IFactory $l10nFactory,
106
-								NewUserMailHelper $newUserMailHelper,
107
-								ISecureRandom $secureRandom,
108
-								RemoteWipe $remoteWipe,
109
-								KnownUserService $knownUserService,
110
-								IEventDispatcher $eventDispatcher) {
111
-		parent::__construct($appName,
112
-							$request,
113
-							$userManager,
114
-							$config,
115
-							$groupManager,
116
-							$userSession,
117
-							$accountManager,
118
-							$l10nFactory);
119
-
120
-		$this->urlGenerator = $urlGenerator;
121
-		$this->logger = $logger;
122
-		$this->l10nFactory = $l10nFactory;
123
-		$this->newUserMailHelper = $newUserMailHelper;
124
-		$this->secureRandom = $secureRandom;
125
-		$this->remoteWipe = $remoteWipe;
126
-		$this->knownUserService = $knownUserService;
127
-		$this->eventDispatcher = $eventDispatcher;
128
-	}
129
-
130
-	/**
131
-	 * @NoAdminRequired
132
-	 *
133
-	 * returns a list of users
134
-	 *
135
-	 * @param string $search
136
-	 * @param int $limit
137
-	 * @param int $offset
138
-	 * @return DataResponse
139
-	 */
140
-	public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
141
-		$user = $this->userSession->getUser();
142
-		$users = [];
143
-
144
-		// Admin? Or SubAdmin?
145
-		$uid = $user->getUID();
146
-		$subAdminManager = $this->groupManager->getSubAdmin();
147
-		if ($this->groupManager->isAdmin($uid)) {
148
-			$users = $this->userManager->search($search, $limit, $offset);
149
-		} elseif ($subAdminManager->isSubAdmin($user)) {
150
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
151
-			foreach ($subAdminOfGroups as $key => $group) {
152
-				$subAdminOfGroups[$key] = $group->getGID();
153
-			}
154
-
155
-			$users = [];
156
-			foreach ($subAdminOfGroups as $group) {
157
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
158
-			}
159
-		}
160
-
161
-		$users = array_keys($users);
162
-
163
-		return new DataResponse([
164
-			'users' => $users
165
-		]);
166
-	}
167
-
168
-	/**
169
-	 * @NoAdminRequired
170
-	 *
171
-	 * returns a list of users and their data
172
-	 */
173
-	public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
174
-		$currentUser = $this->userSession->getUser();
175
-		$users = [];
176
-
177
-		// Admin? Or SubAdmin?
178
-		$uid = $currentUser->getUID();
179
-		$subAdminManager = $this->groupManager->getSubAdmin();
180
-		if ($this->groupManager->isAdmin($uid)) {
181
-			$users = $this->userManager->search($search, $limit, $offset);
182
-			$users = array_keys($users);
183
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
184
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
185
-			foreach ($subAdminOfGroups as $key => $group) {
186
-				$subAdminOfGroups[$key] = $group->getGID();
187
-			}
188
-
189
-			$users = [];
190
-			foreach ($subAdminOfGroups as $group) {
191
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
192
-			}
193
-			$users = array_merge(...$users);
194
-		}
195
-
196
-		$usersDetails = [];
197
-		foreach ($users as $userId) {
198
-			$userId = (string) $userId;
199
-			$userData = $this->getUserData($userId);
200
-			// Do not insert empty entry
201
-			if (!empty($userData)) {
202
-				$usersDetails[$userId] = $userData;
203
-			} else {
204
-				// Logged user does not have permissions to see this user
205
-				// only showing its id
206
-				$usersDetails[$userId] = ['id' => $userId];
207
-			}
208
-		}
209
-
210
-		return new DataResponse([
211
-			'users' => $usersDetails
212
-		]);
213
-	}
214
-
215
-
216
-	/**
217
-	 * @NoAdminRequired
218
-	 * @NoSubAdminRequired
219
-	 *
220
-	 * @param string $location
221
-	 * @param array $search
222
-	 * @return DataResponse
223
-	 */
224
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
225
-		$phoneUtil = PhoneNumberUtil::getInstance();
226
-
227
-		if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
228
-			// Not a valid region code
229
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
230
-		}
231
-
232
-		/** @var IUser $user */
233
-		$user = $this->userSession->getUser();
234
-		$knownTo = $user->getUID();
235
-		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
236
-
237
-		$normalizedNumberToKey = [];
238
-		foreach ($search as $key => $phoneNumbers) {
239
-			foreach ($phoneNumbers as $phone) {
240
-				try {
241
-					$phoneNumber = $phoneUtil->parse($phone, $location);
242
-					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
243
-						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
244
-						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
245
-					}
246
-				} catch (NumberParseException $e) {
247
-				}
248
-
249
-				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
250
-					// If the number has a leading zero (no country code),
251
-					// we also check the default phone region of the instance,
252
-					// when it's different to the user's given region.
253
-					try {
254
-						$phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
255
-						if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
256
-							$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
257
-							$normalizedNumberToKey[$normalizedNumber] = (string) $key;
258
-						}
259
-					} catch (NumberParseException $e) {
260
-					}
261
-				}
262
-			}
263
-		}
264
-
265
-		$phoneNumbers = array_keys($normalizedNumberToKey);
266
-
267
-		if (empty($phoneNumbers)) {
268
-			return new DataResponse();
269
-		}
270
-
271
-		// Cleanup all previous entries and only allow new matches
272
-		$this->knownUserService->deleteKnownTo($knownTo);
273
-
274
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
275
-
276
-		if (empty($userMatches)) {
277
-			return new DataResponse();
278
-		}
279
-
280
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
281
-		if (strpos($cloudUrl, 'http://') === 0) {
282
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
283
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
284
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
285
-		}
286
-
287
-		$matches = [];
288
-		foreach ($userMatches as $phone => $userId) {
289
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
290
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
291
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
292
-		}
293
-
294
-		return new DataResponse($matches);
295
-	}
296
-
297
-	/**
298
-	 * @throws OCSException
299
-	 */
300
-	private function createNewUserId(): string {
301
-		$attempts = 0;
302
-		do {
303
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
304
-			if (!$this->userManager->userExists($uidCandidate)) {
305
-				return $uidCandidate;
306
-			}
307
-			$attempts++;
308
-		} while ($attempts < 10);
309
-		throw new OCSException('Could not create non-existing user id', 111);
310
-	}
311
-
312
-	/**
313
-	 * @PasswordConfirmationRequired
314
-	 * @NoAdminRequired
315
-	 *
316
-	 * @param string $userid
317
-	 * @param string $password
318
-	 * @param string $displayName
319
-	 * @param string $email
320
-	 * @param array $groups
321
-	 * @param array $subadmin
322
-	 * @param string $quota
323
-	 * @param string $language
324
-	 * @return DataResponse
325
-	 * @throws OCSException
326
-	 */
327
-	public function addUser(string $userid,
328
-							string $password = '',
329
-							string $displayName = '',
330
-							string $email = '',
331
-							array $groups = [],
332
-							array $subadmin = [],
333
-							string $quota = '',
334
-							string $language = ''): DataResponse {
335
-		$user = $this->userSession->getUser();
336
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
337
-		$subAdminManager = $this->groupManager->getSubAdmin();
338
-
339
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
340
-			$userid = $this->createNewUserId();
341
-		}
342
-
343
-		if ($this->userManager->userExists($userid)) {
344
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
345
-			throw new OCSException('User already exists', 102);
346
-		}
347
-
348
-		if ($groups !== []) {
349
-			foreach ($groups as $group) {
350
-				if (!$this->groupManager->groupExists($group)) {
351
-					throw new OCSException('group '.$group.' does not exist', 104);
352
-				}
353
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
354
-					throw new OCSException('insufficient privileges for group '. $group, 105);
355
-				}
356
-			}
357
-		} else {
358
-			if (!$isAdmin) {
359
-				throw new OCSException('no group specified (required for subadmins)', 106);
360
-			}
361
-		}
362
-
363
-		$subadminGroups = [];
364
-		if ($subadmin !== []) {
365
-			foreach ($subadmin as $groupid) {
366
-				$group = $this->groupManager->get($groupid);
367
-				// Check if group exists
368
-				if ($group === null) {
369
-					throw new OCSException('Subadmin group does not exist',  102);
370
-				}
371
-				// Check if trying to make subadmin of admin group
372
-				if ($group->getGID() === 'admin') {
373
-					throw new OCSException('Cannot create subadmins for admin group', 103);
374
-				}
375
-				// Check if has permission to promote subadmins
376
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
377
-					throw new OCSForbiddenException('No permissions to promote subadmins');
378
-				}
379
-				$subadminGroups[] = $group;
380
-			}
381
-		}
382
-
383
-		$generatePasswordResetToken = false;
384
-		if ($password === '') {
385
-			if ($email === '') {
386
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
387
-			}
388
-
389
-			$passwordEvent = new GenerateSecurePasswordEvent();
390
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
391
-
392
-			$password = $passwordEvent->getPassword();
393
-			if ($password === null) {
394
-				// Fallback: ensure to pass password_policy in any case
395
-				$password = $this->secureRandom->generate(10)
396
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
397
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
398
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
399
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
400
-			}
401
-			$generatePasswordResetToken = true;
402
-		}
403
-
404
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
405
-			throw new OCSException('Required email address was not provided', 110);
406
-		}
407
-
408
-		try {
409
-			$newUser = $this->userManager->createUser($userid, $password);
410
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
411
-
412
-			foreach ($groups as $group) {
413
-				$this->groupManager->get($group)->addUser($newUser);
414
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
415
-			}
416
-			foreach ($subadminGroups as $group) {
417
-				$subAdminManager->createSubAdmin($newUser, $group);
418
-			}
419
-
420
-			if ($displayName !== '') {
421
-				$this->editUser($userid, 'display', $displayName);
422
-			}
423
-
424
-			if ($quota !== '') {
425
-				$this->editUser($userid, 'quota', $quota);
426
-			}
427
-
428
-			if ($language !== '') {
429
-				$this->editUser($userid, 'language', $language);
430
-			}
431
-
432
-			// Send new user mail only if a mail is set
433
-			if ($email !== '') {
434
-				$newUser->setEMailAddress($email);
435
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
436
-					try {
437
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
438
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
439
-					} catch (\Exception $e) {
440
-						// Mail could be failing hard or just be plain not configured
441
-						// Logging error as it is the hardest of the two
442
-						$this->logger->error("Unable to send the invitation mail to $email",
443
-							[
444
-								'app' => 'ocs_api',
445
-								'exception' => $e,
446
-							]
447
-						);
448
-					}
449
-				}
450
-			}
451
-
452
-			return new DataResponse(['id' => $userid]);
453
-		} catch (HintException $e) {
454
-			$this->logger->warning('Failed addUser attempt with hint exception.',
455
-				[
456
-					'app' => 'ocs_api',
457
-					'exception' => $e,
458
-				]
459
-			);
460
-			throw new OCSException($e->getHint(), 107);
461
-		} catch (OCSException $e) {
462
-			$this->logger->warning('Failed addUser attempt with ocs exeption.',
463
-				[
464
-					'app' => 'ocs_api',
465
-					'exception' => $e,
466
-				]
467
-			);
468
-			throw $e;
469
-		} catch (\InvalidArgumentException $e) {
470
-			$this->logger->error('Failed addUser attempt with invalid argument exeption.',
471
-				[
472
-					'app' => 'ocs_api',
473
-					'exception' => $e,
474
-				]
475
-			);
476
-			throw new OCSException($e->getMessage(), 101);
477
-		} catch (\Exception $e) {
478
-			$this->logger->error('Failed addUser attempt with exception.',
479
-				[
480
-					'app' => 'ocs_api',
481
-					'exception' => $e
482
-				]
483
-			);
484
-			throw new OCSException('Bad request', 101);
485
-		}
486
-	}
487
-
488
-	/**
489
-	 * @NoAdminRequired
490
-	 * @NoSubAdminRequired
491
-	 *
492
-	 * gets user info
493
-	 *
494
-	 * @param string $userId
495
-	 * @return DataResponse
496
-	 * @throws OCSException
497
-	 */
498
-	public function getUser(string $userId): DataResponse {
499
-		$includeScopes = false;
500
-		$currentUser = $this->userSession->getUser();
501
-		if ($currentUser && $currentUser->getUID() === $userId) {
502
-			$includeScopes = true;
503
-		}
504
-
505
-		$data = $this->getUserData($userId, $includeScopes);
506
-		// getUserData returns empty array if not enough permissions
507
-		if (empty($data)) {
508
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
509
-		}
510
-		return new DataResponse($data);
511
-	}
512
-
513
-	/**
514
-	 * @NoAdminRequired
515
-	 * @NoSubAdminRequired
516
-	 *
517
-	 * gets user info from the currently logged in user
518
-	 *
519
-	 * @return DataResponse
520
-	 * @throws OCSException
521
-	 */
522
-	public function getCurrentUser(): DataResponse {
523
-		$user = $this->userSession->getUser();
524
-		if ($user) {
525
-			$data = $this->getUserData($user->getUID(), true);
526
-			// rename "displayname" to "display-name" only for this call to keep
527
-			// the API stable.
528
-			$data['display-name'] = $data['displayname'];
529
-			unset($data['displayname']);
530
-			return new DataResponse($data);
531
-		}
532
-
533
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
534
-	}
535
-
536
-	/**
537
-	 * @NoAdminRequired
538
-	 * @NoSubAdminRequired
539
-	 *
540
-	 * @return DataResponse
541
-	 * @throws OCSException
542
-	 */
543
-	public function getEditableFields(): DataResponse {
544
-		$currentLoggedInUser = $this->userSession->getUser();
545
-		if (!$currentLoggedInUser instanceof IUser) {
546
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
547
-		}
548
-
549
-		return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
550
-	}
551
-
552
-	/**
553
-	 * @NoAdminRequired
554
-	 * @NoSubAdminRequired
555
-	 *
556
-	 * @param string $userId
557
-	 * @return DataResponse
558
-	 * @throws OCSException
559
-	 */
560
-	public function getEditableFieldsForUser(string $userId): DataResponse {
561
-		$currentLoggedInUser = $this->userSession->getUser();
562
-		if (!$currentLoggedInUser instanceof IUser) {
563
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
564
-		}
565
-
566
-		$permittedFields = [];
567
-
568
-		if ($userId !== $currentLoggedInUser->getUID()) {
569
-			$targetUser = $this->userManager->get($userId);
570
-			if (!$targetUser instanceof IUser) {
571
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
572
-			}
573
-
574
-			$subAdminManager = $this->groupManager->getSubAdmin();
575
-			if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
576
-				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
577
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
578
-			}
579
-		} else {
580
-			$targetUser = $currentLoggedInUser;
581
-		}
582
-
583
-		// Editing self (display, email)
584
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
585
-			if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
586
-				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
587
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
588
-			}
589
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
590
-		}
591
-
592
-		$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
593
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
594
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
595
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
596
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
597
-
598
-		return new DataResponse($permittedFields);
599
-	}
600
-
601
-	/**
602
-	 * @NoAdminRequired
603
-	 * @NoSubAdminRequired
604
-	 * @PasswordConfirmationRequired
605
-	 *
606
-	 * @throws OCSException
607
-	 */
608
-	public function editUserMultiValue(
609
-		string $userId,
610
-		string $collectionName,
611
-		string $key,
612
-		string $value
613
-	): DataResponse {
614
-		$currentLoggedInUser = $this->userSession->getUser();
615
-		if ($currentLoggedInUser === null) {
616
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
617
-		}
618
-
619
-		$targetUser = $this->userManager->get($userId);
620
-		if ($targetUser === null) {
621
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
622
-		}
623
-
624
-		$permittedFields = [];
625
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
626
-			// Editing self (display, email)
627
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
628
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
629
-		} else {
630
-			// Check if admin / subadmin
631
-			$subAdminManager = $this->groupManager->getSubAdmin();
632
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
633
-				|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
634
-				// They have permissions over the user
635
-
636
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
637
-			} else {
638
-				// No rights
639
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
640
-			}
641
-		}
642
-
643
-		// Check if permitted to edit this field
644
-		if (!in_array($collectionName, $permittedFields)) {
645
-			throw new OCSException('', 103);
646
-		}
647
-
648
-		switch ($collectionName) {
649
-			case IAccountManager::COLLECTION_EMAIL:
650
-				$userAccount = $this->accountManager->getAccount($targetUser);
651
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
652
-				$mailCollection->removePropertyByValue($key);
653
-				if ($value !== '') {
654
-					$mailCollection->addPropertyWithDefaults($value);
655
-				}
656
-				$this->accountManager->updateAccount($userAccount);
657
-				break;
658
-
659
-			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
660
-				$userAccount = $this->accountManager->getAccount($targetUser);
661
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
662
-				$targetProperty = null;
663
-				foreach ($mailCollection->getProperties() as $property) {
664
-					if ($property->getValue() === $key) {
665
-						$targetProperty = $property;
666
-						break;
667
-					}
668
-				}
669
-				if ($targetProperty instanceof IAccountProperty) {
670
-					try {
671
-						$targetProperty->setScope($value);
672
-						$this->accountManager->updateAccount($userAccount);
673
-					} catch (\InvalidArgumentException $e) {
674
-						throw new OCSException('', 102);
675
-					}
676
-				} else {
677
-					throw new OCSException('', 102);
678
-				}
679
-				break;
680
-
681
-			default:
682
-				throw new OCSException('', 103);
683
-		}
684
-		return new DataResponse();
685
-	}
686
-
687
-	/**
688
-	 * @NoAdminRequired
689
-	 * @NoSubAdminRequired
690
-	 * @PasswordConfirmationRequired
691
-	 *
692
-	 * edit users
693
-	 *
694
-	 * @param string $userId
695
-	 * @param string $key
696
-	 * @param string $value
697
-	 * @return DataResponse
698
-	 * @throws OCSException
699
-	 */
700
-	public function editUser(string $userId, string $key, string $value): DataResponse {
701
-		$currentLoggedInUser = $this->userSession->getUser();
702
-
703
-		$targetUser = $this->userManager->get($userId);
704
-		if ($targetUser === null) {
705
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
706
-		}
707
-
708
-		$permittedFields = [];
709
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
710
-			// Editing self (display, email)
711
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
712
-				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
713
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
714
-					$permittedFields[] = 'display';
715
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
716
-				}
717
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
718
-			}
719
-
720
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
721
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
722
-
723
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
724
-
725
-			$permittedFields[] = 'password';
726
-			if ($this->config->getSystemValue('force_language', false) === false ||
727
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
728
-				$permittedFields[] = 'language';
729
-			}
730
-
731
-			if ($this->config->getSystemValue('force_locale', false) === false ||
732
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
733
-				$permittedFields[] = 'locale';
734
-			}
735
-
736
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
737
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
738
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
739
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
740
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
741
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
742
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
743
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
744
-
745
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
746
-
747
-			// If admin they can edit their own quota
748
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
749
-				$permittedFields[] = 'quota';
750
-			}
751
-		} else {
752
-			// Check if admin / subadmin
753
-			$subAdminManager = $this->groupManager->getSubAdmin();
754
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
755
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
756
-				// They have permissions over the user
757
-				if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
758
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
759
-					$permittedFields[] = 'display';
760
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
761
-				}
762
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
763
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
764
-				$permittedFields[] = 'password';
765
-				$permittedFields[] = 'language';
766
-				$permittedFields[] = 'locale';
767
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
768
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
769
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
770
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
771
-				$permittedFields[] = 'quota';
772
-			} else {
773
-				// No rights
774
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
775
-			}
776
-		}
777
-		// Check if permitted to edit this field
778
-		if (!in_array($key, $permittedFields)) {
779
-			throw new OCSException('', 103);
780
-		}
781
-		// Process the edit
782
-		switch ($key) {
783
-			case 'display':
784
-			case IAccountManager::PROPERTY_DISPLAYNAME:
785
-				$targetUser->setDisplayName($value);
786
-				break;
787
-			case 'quota':
788
-				$quota = $value;
789
-				if ($quota !== 'none' && $quota !== 'default') {
790
-					if (is_numeric($quota)) {
791
-						$quota = (float) $quota;
792
-					} else {
793
-						$quota = \OCP\Util::computerFileSize($quota);
794
-					}
795
-					if ($quota === false) {
796
-						throw new OCSException('Invalid quota value '.$value, 102);
797
-					}
798
-					if ($quota === -1) {
799
-						$quota = 'none';
800
-					} else {
801
-						$quota = \OCP\Util::humanFileSize($quota);
802
-					}
803
-				}
804
-				$targetUser->setQuota($quota);
805
-				break;
806
-			case 'password':
807
-				try {
808
-					if (!$targetUser->canChangePassword()) {
809
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
810
-					}
811
-					$targetUser->setPassword($value);
812
-				} catch (HintException $e) { // password policy error
813
-					throw new OCSException($e->getMessage(), 103);
814
-				}
815
-				break;
816
-			case 'language':
817
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
818
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
819
-					throw new OCSException('Invalid language', 102);
820
-				}
821
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
822
-				break;
823
-			case 'locale':
824
-				if (!$this->l10nFactory->localeExists($value)) {
825
-					throw new OCSException('Invalid locale', 102);
826
-				}
827
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
828
-				break;
829
-			case IAccountManager::PROPERTY_EMAIL:
830
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
831
-					$targetUser->setEMailAddress($value);
832
-				} else {
833
-					throw new OCSException('', 102);
834
-				}
835
-				break;
836
-			case IAccountManager::COLLECTION_EMAIL:
837
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getEMailAddress()) {
838
-					$userAccount = $this->accountManager->getAccount($targetUser);
839
-					$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
840
-					foreach ($mailCollection->getProperties() as $property) {
841
-						if ($property->getValue() === $value) {
842
-							break;
843
-						}
844
-					}
845
-					$mailCollection->addPropertyWithDefaults($value);
846
-					$this->accountManager->updateAccount($userAccount);
847
-				} else {
848
-					throw new OCSException('', 102);
849
-				}
850
-				break;
851
-			case IAccountManager::PROPERTY_PHONE:
852
-			case IAccountManager::PROPERTY_ADDRESS:
853
-			case IAccountManager::PROPERTY_WEBSITE:
854
-			case IAccountManager::PROPERTY_TWITTER:
855
-				$userAccount = $this->accountManager->getAccount($targetUser);
856
-				try {
857
-					$userProperty = $userAccount->getProperty($key);
858
-					if ($userProperty->getValue() !== $value) {
859
-						try {
860
-							$userProperty->setValue($value);
861
-							if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
862
-								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
863
-							}
864
-						} catch (\InvalidArgumentException $e) {
865
-							throw new OCSException('Invalid ' . $e->getMessage(), 102);
866
-						}
867
-					}
868
-				} catch (PropertyDoesNotExistException $e) {
869
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
870
-				}
871
-				$this->accountManager->updateAccount($userAccount);
872
-				break;
873
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
874
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
875
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
876
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
877
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
878
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
879
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
880
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
881
-				$userAccount = $this->accountManager->getAccount($targetUser);
882
-				$userProperty = $userAccount->getProperty($propertyName);
883
-				if ($userProperty->getScope() !== $value) {
884
-					try {
885
-						$userProperty->setScope($value);
886
-						$this->accountManager->updateAccount($userAccount);
887
-					} catch (\InvalidArgumentException $e) {
888
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
889
-					}
890
-				}
891
-				break;
892
-			default:
893
-				throw new OCSException('', 103);
894
-		}
895
-		return new DataResponse();
896
-	}
897
-
898
-	/**
899
-	 * @PasswordConfirmationRequired
900
-	 * @NoAdminRequired
901
-	 *
902
-	 * @param string $userId
903
-	 *
904
-	 * @return DataResponse
905
-	 *
906
-	 * @throws OCSException
907
-	 */
908
-	public function wipeUserDevices(string $userId): DataResponse {
909
-		/** @var IUser $currentLoggedInUser */
910
-		$currentLoggedInUser = $this->userSession->getUser();
911
-
912
-		$targetUser = $this->userManager->get($userId);
913
-
914
-		if ($targetUser === null) {
915
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
916
-		}
917
-
918
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
919
-			throw new OCSException('', 101);
920
-		}
921
-
922
-		// If not permitted
923
-		$subAdminManager = $this->groupManager->getSubAdmin();
924
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
925
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
926
-		}
927
-
928
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
929
-
930
-		return new DataResponse();
931
-	}
932
-
933
-	/**
934
-	 * @PasswordConfirmationRequired
935
-	 * @NoAdminRequired
936
-	 *
937
-	 * @param string $userId
938
-	 * @return DataResponse
939
-	 * @throws OCSException
940
-	 */
941
-	public function deleteUser(string $userId): DataResponse {
942
-		$currentLoggedInUser = $this->userSession->getUser();
943
-
944
-		$targetUser = $this->userManager->get($userId);
945
-
946
-		if ($targetUser === null) {
947
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
948
-		}
949
-
950
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
951
-			throw new OCSException('', 101);
952
-		}
953
-
954
-		// If not permitted
955
-		$subAdminManager = $this->groupManager->getSubAdmin();
956
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
957
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
958
-		}
959
-
960
-		// Go ahead with the delete
961
-		if ($targetUser->delete()) {
962
-			return new DataResponse();
963
-		} else {
964
-			throw new OCSException('', 101);
965
-		}
966
-	}
967
-
968
-	/**
969
-	 * @PasswordConfirmationRequired
970
-	 * @NoAdminRequired
971
-	 *
972
-	 * @param string $userId
973
-	 * @return DataResponse
974
-	 * @throws OCSException
975
-	 * @throws OCSForbiddenException
976
-	 */
977
-	public function disableUser(string $userId): DataResponse {
978
-		return $this->setEnabled($userId, false);
979
-	}
980
-
981
-	/**
982
-	 * @PasswordConfirmationRequired
983
-	 * @NoAdminRequired
984
-	 *
985
-	 * @param string $userId
986
-	 * @return DataResponse
987
-	 * @throws OCSException
988
-	 * @throws OCSForbiddenException
989
-	 */
990
-	public function enableUser(string $userId): DataResponse {
991
-		return $this->setEnabled($userId, true);
992
-	}
993
-
994
-	/**
995
-	 * @param string $userId
996
-	 * @param bool $value
997
-	 * @return DataResponse
998
-	 * @throws OCSException
999
-	 */
1000
-	private function setEnabled(string $userId, bool $value): DataResponse {
1001
-		$currentLoggedInUser = $this->userSession->getUser();
1002
-
1003
-		$targetUser = $this->userManager->get($userId);
1004
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1005
-			throw new OCSException('', 101);
1006
-		}
1007
-
1008
-		// If not permitted
1009
-		$subAdminManager = $this->groupManager->getSubAdmin();
1010
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1011
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1012
-		}
1013
-
1014
-		// enable/disable the user now
1015
-		$targetUser->setEnabled($value);
1016
-		return new DataResponse();
1017
-	}
1018
-
1019
-	/**
1020
-	 * @NoAdminRequired
1021
-	 * @NoSubAdminRequired
1022
-	 *
1023
-	 * @param string $userId
1024
-	 * @return DataResponse
1025
-	 * @throws OCSException
1026
-	 */
1027
-	public function getUsersGroups(string $userId): DataResponse {
1028
-		$loggedInUser = $this->userSession->getUser();
1029
-
1030
-		$targetUser = $this->userManager->get($userId);
1031
-		if ($targetUser === null) {
1032
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1033
-		}
1034
-
1035
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
1036
-			// Self lookup or admin lookup
1037
-			return new DataResponse([
1038
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
1039
-			]);
1040
-		} else {
1041
-			$subAdminManager = $this->groupManager->getSubAdmin();
1042
-
1043
-			// Looking up someone else
1044
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1045
-				// Return the group that the method caller is subadmin of for the user in question
1046
-				/** @var IGroup[] $getSubAdminsGroups */
1047
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1048
-				foreach ($getSubAdminsGroups as $key => $group) {
1049
-					$getSubAdminsGroups[$key] = $group->getGID();
1050
-				}
1051
-				$groups = array_intersect(
1052
-					$getSubAdminsGroups,
1053
-					$this->groupManager->getUserGroupIds($targetUser)
1054
-				);
1055
-				return new DataResponse(['groups' => $groups]);
1056
-			} else {
1057
-				// Not permitted
1058
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1059
-			}
1060
-		}
1061
-	}
1062
-
1063
-	/**
1064
-	 * @PasswordConfirmationRequired
1065
-	 * @NoAdminRequired
1066
-	 *
1067
-	 * @param string $userId
1068
-	 * @param string $groupid
1069
-	 * @return DataResponse
1070
-	 * @throws OCSException
1071
-	 */
1072
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1073
-		if ($groupid === '') {
1074
-			throw new OCSException('', 101);
1075
-		}
1076
-
1077
-		$group = $this->groupManager->get($groupid);
1078
-		$targetUser = $this->userManager->get($userId);
1079
-		if ($group === null) {
1080
-			throw new OCSException('', 102);
1081
-		}
1082
-		if ($targetUser === null) {
1083
-			throw new OCSException('', 103);
1084
-		}
1085
-
1086
-		// If they're not an admin, check they are a subadmin of the group in question
1087
-		$loggedInUser = $this->userSession->getUser();
1088
-		$subAdminManager = $this->groupManager->getSubAdmin();
1089
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1090
-			throw new OCSException('', 104);
1091
-		}
1092
-
1093
-		// Add user to group
1094
-		$group->addUser($targetUser);
1095
-		return new DataResponse();
1096
-	}
1097
-
1098
-	/**
1099
-	 * @PasswordConfirmationRequired
1100
-	 * @NoAdminRequired
1101
-	 *
1102
-	 * @param string $userId
1103
-	 * @param string $groupid
1104
-	 * @return DataResponse
1105
-	 * @throws OCSException
1106
-	 */
1107
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
1108
-		$loggedInUser = $this->userSession->getUser();
1109
-
1110
-		if ($groupid === null || trim($groupid) === '') {
1111
-			throw new OCSException('', 101);
1112
-		}
1113
-
1114
-		$group = $this->groupManager->get($groupid);
1115
-		if ($group === null) {
1116
-			throw new OCSException('', 102);
1117
-		}
1118
-
1119
-		$targetUser = $this->userManager->get($userId);
1120
-		if ($targetUser === null) {
1121
-			throw new OCSException('', 103);
1122
-		}
1123
-
1124
-		// If they're not an admin, check they are a subadmin of the group in question
1125
-		$subAdminManager = $this->groupManager->getSubAdmin();
1126
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1127
-			throw new OCSException('', 104);
1128
-		}
1129
-
1130
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1131
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1132
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1133
-				if ($group->getGID() === 'admin') {
1134
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
1135
-				}
1136
-			} else {
1137
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1138
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1139
-			}
1140
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1141
-			/** @var IGroup[] $subAdminGroups */
1142
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1143
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1144
-				return $subAdminGroup->getGID();
1145
-			}, $subAdminGroups);
1146
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1147
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1148
-
1149
-			if (count($userSubAdminGroups) <= 1) {
1150
-				// Subadmin must not be able to remove a user from all their subadmin groups.
1151
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1152
-			}
1153
-		}
1154
-
1155
-		// Remove user from group
1156
-		$group->removeUser($targetUser);
1157
-		return new DataResponse();
1158
-	}
1159
-
1160
-	/**
1161
-	 * Creates a subadmin
1162
-	 *
1163
-	 * @PasswordConfirmationRequired
1164
-	 *
1165
-	 * @param string $userId
1166
-	 * @param string $groupid
1167
-	 * @return DataResponse
1168
-	 * @throws OCSException
1169
-	 */
1170
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1171
-		$group = $this->groupManager->get($groupid);
1172
-		$user = $this->userManager->get($userId);
1173
-
1174
-		// Check if the user exists
1175
-		if ($user === null) {
1176
-			throw new OCSException('User does not exist', 101);
1177
-		}
1178
-		// Check if group exists
1179
-		if ($group === null) {
1180
-			throw new OCSException('Group does not exist',  102);
1181
-		}
1182
-		// Check if trying to make subadmin of admin group
1183
-		if ($group->getGID() === 'admin') {
1184
-			throw new OCSException('Cannot create subadmins for admin group', 103);
1185
-		}
1186
-
1187
-		$subAdminManager = $this->groupManager->getSubAdmin();
1188
-
1189
-		// We cannot be subadmin twice
1190
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1191
-			return new DataResponse();
1192
-		}
1193
-		// Go
1194
-		$subAdminManager->createSubAdmin($user, $group);
1195
-		return new DataResponse();
1196
-	}
1197
-
1198
-	/**
1199
-	 * Removes a subadmin from a group
1200
-	 *
1201
-	 * @PasswordConfirmationRequired
1202
-	 *
1203
-	 * @param string $userId
1204
-	 * @param string $groupid
1205
-	 * @return DataResponse
1206
-	 * @throws OCSException
1207
-	 */
1208
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1209
-		$group = $this->groupManager->get($groupid);
1210
-		$user = $this->userManager->get($userId);
1211
-		$subAdminManager = $this->groupManager->getSubAdmin();
1212
-
1213
-		// Check if the user exists
1214
-		if ($user === null) {
1215
-			throw new OCSException('User does not exist', 101);
1216
-		}
1217
-		// Check if the group exists
1218
-		if ($group === null) {
1219
-			throw new OCSException('Group does not exist', 101);
1220
-		}
1221
-		// Check if they are a subadmin of this said group
1222
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1223
-			throw new OCSException('User is not a subadmin of this group', 102);
1224
-		}
1225
-
1226
-		// Go
1227
-		$subAdminManager->deleteSubAdmin($user, $group);
1228
-		return new DataResponse();
1229
-	}
1230
-
1231
-	/**
1232
-	 * Get the groups a user is a subadmin of
1233
-	 *
1234
-	 * @param string $userId
1235
-	 * @return DataResponse
1236
-	 * @throws OCSException
1237
-	 */
1238
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1239
-		$groups = $this->getUserSubAdminGroupsData($userId);
1240
-		return new DataResponse($groups);
1241
-	}
1242
-
1243
-	/**
1244
-	 * @NoAdminRequired
1245
-	 * @PasswordConfirmationRequired
1246
-	 *
1247
-	 * resend welcome message
1248
-	 *
1249
-	 * @param string $userId
1250
-	 * @return DataResponse
1251
-	 * @throws OCSException
1252
-	 */
1253
-	public function resendWelcomeMessage(string $userId): DataResponse {
1254
-		$currentLoggedInUser = $this->userSession->getUser();
1255
-
1256
-		$targetUser = $this->userManager->get($userId);
1257
-		if ($targetUser === null) {
1258
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1259
-		}
1260
-
1261
-		// Check if admin / subadmin
1262
-		$subAdminManager = $this->groupManager->getSubAdmin();
1263
-		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1264
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1265
-			// No rights
1266
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1267
-		}
1268
-
1269
-		$email = $targetUser->getEMailAddress();
1270
-		if ($email === '' || $email === null) {
1271
-			throw new OCSException('Email address not available', 101);
1272
-		}
1273
-
1274
-		try {
1275
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1276
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1277
-		} catch (\Exception $e) {
1278
-			$this->logger->error("Can't send new user mail to $email",
1279
-				[
1280
-					'app' => 'settings',
1281
-					'exception' => $e,
1282
-				]
1283
-			);
1284
-			throw new OCSException('Sending email failed', 102);
1285
-		}
1286
-
1287
-		return new DataResponse();
1288
-	}
79
+    /** @var IURLGenerator */
80
+    protected $urlGenerator;
81
+    /** @var LoggerInterface */
82
+    private $logger;
83
+    /** @var IFactory */
84
+    protected $l10nFactory;
85
+    /** @var NewUserMailHelper */
86
+    private $newUserMailHelper;
87
+    /** @var ISecureRandom */
88
+    private $secureRandom;
89
+    /** @var RemoteWipe */
90
+    private $remoteWipe;
91
+    /** @var KnownUserService */
92
+    private $knownUserService;
93
+    /** @var IEventDispatcher */
94
+    private $eventDispatcher;
95
+
96
+    public function __construct(string $appName,
97
+                                IRequest $request,
98
+                                IUserManager $userManager,
99
+                                IConfig $config,
100
+                                IGroupManager $groupManager,
101
+                                IUserSession $userSession,
102
+                                IAccountManager $accountManager,
103
+                                IURLGenerator $urlGenerator,
104
+                                LoggerInterface $logger,
105
+                                IFactory $l10nFactory,
106
+                                NewUserMailHelper $newUserMailHelper,
107
+                                ISecureRandom $secureRandom,
108
+                                RemoteWipe $remoteWipe,
109
+                                KnownUserService $knownUserService,
110
+                                IEventDispatcher $eventDispatcher) {
111
+        parent::__construct($appName,
112
+                            $request,
113
+                            $userManager,
114
+                            $config,
115
+                            $groupManager,
116
+                            $userSession,
117
+                            $accountManager,
118
+                            $l10nFactory);
119
+
120
+        $this->urlGenerator = $urlGenerator;
121
+        $this->logger = $logger;
122
+        $this->l10nFactory = $l10nFactory;
123
+        $this->newUserMailHelper = $newUserMailHelper;
124
+        $this->secureRandom = $secureRandom;
125
+        $this->remoteWipe = $remoteWipe;
126
+        $this->knownUserService = $knownUserService;
127
+        $this->eventDispatcher = $eventDispatcher;
128
+    }
129
+
130
+    /**
131
+     * @NoAdminRequired
132
+     *
133
+     * returns a list of users
134
+     *
135
+     * @param string $search
136
+     * @param int $limit
137
+     * @param int $offset
138
+     * @return DataResponse
139
+     */
140
+    public function getUsers(string $search = '', int $limit = null, int $offset = 0): DataResponse {
141
+        $user = $this->userSession->getUser();
142
+        $users = [];
143
+
144
+        // Admin? Or SubAdmin?
145
+        $uid = $user->getUID();
146
+        $subAdminManager = $this->groupManager->getSubAdmin();
147
+        if ($this->groupManager->isAdmin($uid)) {
148
+            $users = $this->userManager->search($search, $limit, $offset);
149
+        } elseif ($subAdminManager->isSubAdmin($user)) {
150
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
151
+            foreach ($subAdminOfGroups as $key => $group) {
152
+                $subAdminOfGroups[$key] = $group->getGID();
153
+            }
154
+
155
+            $users = [];
156
+            foreach ($subAdminOfGroups as $group) {
157
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
158
+            }
159
+        }
160
+
161
+        $users = array_keys($users);
162
+
163
+        return new DataResponse([
164
+            'users' => $users
165
+        ]);
166
+    }
167
+
168
+    /**
169
+     * @NoAdminRequired
170
+     *
171
+     * returns a list of users and their data
172
+     */
173
+    public function getUsersDetails(string $search = '', int $limit = null, int $offset = 0): DataResponse {
174
+        $currentUser = $this->userSession->getUser();
175
+        $users = [];
176
+
177
+        // Admin? Or SubAdmin?
178
+        $uid = $currentUser->getUID();
179
+        $subAdminManager = $this->groupManager->getSubAdmin();
180
+        if ($this->groupManager->isAdmin($uid)) {
181
+            $users = $this->userManager->search($search, $limit, $offset);
182
+            $users = array_keys($users);
183
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
184
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
185
+            foreach ($subAdminOfGroups as $key => $group) {
186
+                $subAdminOfGroups[$key] = $group->getGID();
187
+            }
188
+
189
+            $users = [];
190
+            foreach ($subAdminOfGroups as $group) {
191
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
192
+            }
193
+            $users = array_merge(...$users);
194
+        }
195
+
196
+        $usersDetails = [];
197
+        foreach ($users as $userId) {
198
+            $userId = (string) $userId;
199
+            $userData = $this->getUserData($userId);
200
+            // Do not insert empty entry
201
+            if (!empty($userData)) {
202
+                $usersDetails[$userId] = $userData;
203
+            } else {
204
+                // Logged user does not have permissions to see this user
205
+                // only showing its id
206
+                $usersDetails[$userId] = ['id' => $userId];
207
+            }
208
+        }
209
+
210
+        return new DataResponse([
211
+            'users' => $usersDetails
212
+        ]);
213
+    }
214
+
215
+
216
+    /**
217
+     * @NoAdminRequired
218
+     * @NoSubAdminRequired
219
+     *
220
+     * @param string $location
221
+     * @param array $search
222
+     * @return DataResponse
223
+     */
224
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
225
+        $phoneUtil = PhoneNumberUtil::getInstance();
226
+
227
+        if ($phoneUtil->getCountryCodeForRegion($location) === 0) {
228
+            // Not a valid region code
229
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
230
+        }
231
+
232
+        /** @var IUser $user */
233
+        $user = $this->userSession->getUser();
234
+        $knownTo = $user->getUID();
235
+        $defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
236
+
237
+        $normalizedNumberToKey = [];
238
+        foreach ($search as $key => $phoneNumbers) {
239
+            foreach ($phoneNumbers as $phone) {
240
+                try {
241
+                    $phoneNumber = $phoneUtil->parse($phone, $location);
242
+                    if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
243
+                        $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
244
+                        $normalizedNumberToKey[$normalizedNumber] = (string) $key;
245
+                    }
246
+                } catch (NumberParseException $e) {
247
+                }
248
+
249
+                if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && strpos($phone, '0') === 0) {
250
+                    // If the number has a leading zero (no country code),
251
+                    // we also check the default phone region of the instance,
252
+                    // when it's different to the user's given region.
253
+                    try {
254
+                        $phoneNumber = $phoneUtil->parse($phone, $defaultPhoneRegion);
255
+                        if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
256
+                            $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
257
+                            $normalizedNumberToKey[$normalizedNumber] = (string) $key;
258
+                        }
259
+                    } catch (NumberParseException $e) {
260
+                    }
261
+                }
262
+            }
263
+        }
264
+
265
+        $phoneNumbers = array_keys($normalizedNumberToKey);
266
+
267
+        if (empty($phoneNumbers)) {
268
+            return new DataResponse();
269
+        }
270
+
271
+        // Cleanup all previous entries and only allow new matches
272
+        $this->knownUserService->deleteKnownTo($knownTo);
273
+
274
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
275
+
276
+        if (empty($userMatches)) {
277
+            return new DataResponse();
278
+        }
279
+
280
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
281
+        if (strpos($cloudUrl, 'http://') === 0) {
282
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
283
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
284
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
285
+        }
286
+
287
+        $matches = [];
288
+        foreach ($userMatches as $phone => $userId) {
289
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
290
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
291
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
292
+        }
293
+
294
+        return new DataResponse($matches);
295
+    }
296
+
297
+    /**
298
+     * @throws OCSException
299
+     */
300
+    private function createNewUserId(): string {
301
+        $attempts = 0;
302
+        do {
303
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
304
+            if (!$this->userManager->userExists($uidCandidate)) {
305
+                return $uidCandidate;
306
+            }
307
+            $attempts++;
308
+        } while ($attempts < 10);
309
+        throw new OCSException('Could not create non-existing user id', 111);
310
+    }
311
+
312
+    /**
313
+     * @PasswordConfirmationRequired
314
+     * @NoAdminRequired
315
+     *
316
+     * @param string $userid
317
+     * @param string $password
318
+     * @param string $displayName
319
+     * @param string $email
320
+     * @param array $groups
321
+     * @param array $subadmin
322
+     * @param string $quota
323
+     * @param string $language
324
+     * @return DataResponse
325
+     * @throws OCSException
326
+     */
327
+    public function addUser(string $userid,
328
+                            string $password = '',
329
+                            string $displayName = '',
330
+                            string $email = '',
331
+                            array $groups = [],
332
+                            array $subadmin = [],
333
+                            string $quota = '',
334
+                            string $language = ''): DataResponse {
335
+        $user = $this->userSession->getUser();
336
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
337
+        $subAdminManager = $this->groupManager->getSubAdmin();
338
+
339
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
340
+            $userid = $this->createNewUserId();
341
+        }
342
+
343
+        if ($this->userManager->userExists($userid)) {
344
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
345
+            throw new OCSException('User already exists', 102);
346
+        }
347
+
348
+        if ($groups !== []) {
349
+            foreach ($groups as $group) {
350
+                if (!$this->groupManager->groupExists($group)) {
351
+                    throw new OCSException('group '.$group.' does not exist', 104);
352
+                }
353
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
354
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
355
+                }
356
+            }
357
+        } else {
358
+            if (!$isAdmin) {
359
+                throw new OCSException('no group specified (required for subadmins)', 106);
360
+            }
361
+        }
362
+
363
+        $subadminGroups = [];
364
+        if ($subadmin !== []) {
365
+            foreach ($subadmin as $groupid) {
366
+                $group = $this->groupManager->get($groupid);
367
+                // Check if group exists
368
+                if ($group === null) {
369
+                    throw new OCSException('Subadmin group does not exist',  102);
370
+                }
371
+                // Check if trying to make subadmin of admin group
372
+                if ($group->getGID() === 'admin') {
373
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
374
+                }
375
+                // Check if has permission to promote subadmins
376
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
377
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
378
+                }
379
+                $subadminGroups[] = $group;
380
+            }
381
+        }
382
+
383
+        $generatePasswordResetToken = false;
384
+        if ($password === '') {
385
+            if ($email === '') {
386
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
387
+            }
388
+
389
+            $passwordEvent = new GenerateSecurePasswordEvent();
390
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
391
+
392
+            $password = $passwordEvent->getPassword();
393
+            if ($password === null) {
394
+                // Fallback: ensure to pass password_policy in any case
395
+                $password = $this->secureRandom->generate(10)
396
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
397
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
398
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
399
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
400
+            }
401
+            $generatePasswordResetToken = true;
402
+        }
403
+
404
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
405
+            throw new OCSException('Required email address was not provided', 110);
406
+        }
407
+
408
+        try {
409
+            $newUser = $this->userManager->createUser($userid, $password);
410
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
411
+
412
+            foreach ($groups as $group) {
413
+                $this->groupManager->get($group)->addUser($newUser);
414
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
415
+            }
416
+            foreach ($subadminGroups as $group) {
417
+                $subAdminManager->createSubAdmin($newUser, $group);
418
+            }
419
+
420
+            if ($displayName !== '') {
421
+                $this->editUser($userid, 'display', $displayName);
422
+            }
423
+
424
+            if ($quota !== '') {
425
+                $this->editUser($userid, 'quota', $quota);
426
+            }
427
+
428
+            if ($language !== '') {
429
+                $this->editUser($userid, 'language', $language);
430
+            }
431
+
432
+            // Send new user mail only if a mail is set
433
+            if ($email !== '') {
434
+                $newUser->setEMailAddress($email);
435
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
436
+                    try {
437
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
438
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
439
+                    } catch (\Exception $e) {
440
+                        // Mail could be failing hard or just be plain not configured
441
+                        // Logging error as it is the hardest of the two
442
+                        $this->logger->error("Unable to send the invitation mail to $email",
443
+                            [
444
+                                'app' => 'ocs_api',
445
+                                'exception' => $e,
446
+                            ]
447
+                        );
448
+                    }
449
+                }
450
+            }
451
+
452
+            return new DataResponse(['id' => $userid]);
453
+        } catch (HintException $e) {
454
+            $this->logger->warning('Failed addUser attempt with hint exception.',
455
+                [
456
+                    'app' => 'ocs_api',
457
+                    'exception' => $e,
458
+                ]
459
+            );
460
+            throw new OCSException($e->getHint(), 107);
461
+        } catch (OCSException $e) {
462
+            $this->logger->warning('Failed addUser attempt with ocs exeption.',
463
+                [
464
+                    'app' => 'ocs_api',
465
+                    'exception' => $e,
466
+                ]
467
+            );
468
+            throw $e;
469
+        } catch (\InvalidArgumentException $e) {
470
+            $this->logger->error('Failed addUser attempt with invalid argument exeption.',
471
+                [
472
+                    'app' => 'ocs_api',
473
+                    'exception' => $e,
474
+                ]
475
+            );
476
+            throw new OCSException($e->getMessage(), 101);
477
+        } catch (\Exception $e) {
478
+            $this->logger->error('Failed addUser attempt with exception.',
479
+                [
480
+                    'app' => 'ocs_api',
481
+                    'exception' => $e
482
+                ]
483
+            );
484
+            throw new OCSException('Bad request', 101);
485
+        }
486
+    }
487
+
488
+    /**
489
+     * @NoAdminRequired
490
+     * @NoSubAdminRequired
491
+     *
492
+     * gets user info
493
+     *
494
+     * @param string $userId
495
+     * @return DataResponse
496
+     * @throws OCSException
497
+     */
498
+    public function getUser(string $userId): DataResponse {
499
+        $includeScopes = false;
500
+        $currentUser = $this->userSession->getUser();
501
+        if ($currentUser && $currentUser->getUID() === $userId) {
502
+            $includeScopes = true;
503
+        }
504
+
505
+        $data = $this->getUserData($userId, $includeScopes);
506
+        // getUserData returns empty array if not enough permissions
507
+        if (empty($data)) {
508
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
509
+        }
510
+        return new DataResponse($data);
511
+    }
512
+
513
+    /**
514
+     * @NoAdminRequired
515
+     * @NoSubAdminRequired
516
+     *
517
+     * gets user info from the currently logged in user
518
+     *
519
+     * @return DataResponse
520
+     * @throws OCSException
521
+     */
522
+    public function getCurrentUser(): DataResponse {
523
+        $user = $this->userSession->getUser();
524
+        if ($user) {
525
+            $data = $this->getUserData($user->getUID(), true);
526
+            // rename "displayname" to "display-name" only for this call to keep
527
+            // the API stable.
528
+            $data['display-name'] = $data['displayname'];
529
+            unset($data['displayname']);
530
+            return new DataResponse($data);
531
+        }
532
+
533
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
534
+    }
535
+
536
+    /**
537
+     * @NoAdminRequired
538
+     * @NoSubAdminRequired
539
+     *
540
+     * @return DataResponse
541
+     * @throws OCSException
542
+     */
543
+    public function getEditableFields(): DataResponse {
544
+        $currentLoggedInUser = $this->userSession->getUser();
545
+        if (!$currentLoggedInUser instanceof IUser) {
546
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
547
+        }
548
+
549
+        return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
550
+    }
551
+
552
+    /**
553
+     * @NoAdminRequired
554
+     * @NoSubAdminRequired
555
+     *
556
+     * @param string $userId
557
+     * @return DataResponse
558
+     * @throws OCSException
559
+     */
560
+    public function getEditableFieldsForUser(string $userId): DataResponse {
561
+        $currentLoggedInUser = $this->userSession->getUser();
562
+        if (!$currentLoggedInUser instanceof IUser) {
563
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
564
+        }
565
+
566
+        $permittedFields = [];
567
+
568
+        if ($userId !== $currentLoggedInUser->getUID()) {
569
+            $targetUser = $this->userManager->get($userId);
570
+            if (!$targetUser instanceof IUser) {
571
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
572
+            }
573
+
574
+            $subAdminManager = $this->groupManager->getSubAdmin();
575
+            if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
576
+                && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
577
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
578
+            }
579
+        } else {
580
+            $targetUser = $currentLoggedInUser;
581
+        }
582
+
583
+        // Editing self (display, email)
584
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
585
+            if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
586
+                || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
587
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
588
+            }
589
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
590
+        }
591
+
592
+        $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
593
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
594
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
595
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
596
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
597
+
598
+        return new DataResponse($permittedFields);
599
+    }
600
+
601
+    /**
602
+     * @NoAdminRequired
603
+     * @NoSubAdminRequired
604
+     * @PasswordConfirmationRequired
605
+     *
606
+     * @throws OCSException
607
+     */
608
+    public function editUserMultiValue(
609
+        string $userId,
610
+        string $collectionName,
611
+        string $key,
612
+        string $value
613
+    ): DataResponse {
614
+        $currentLoggedInUser = $this->userSession->getUser();
615
+        if ($currentLoggedInUser === null) {
616
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
617
+        }
618
+
619
+        $targetUser = $this->userManager->get($userId);
620
+        if ($targetUser === null) {
621
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
622
+        }
623
+
624
+        $permittedFields = [];
625
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
626
+            // Editing self (display, email)
627
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
628
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
629
+        } else {
630
+            // Check if admin / subadmin
631
+            $subAdminManager = $this->groupManager->getSubAdmin();
632
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
633
+                || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
634
+                // They have permissions over the user
635
+
636
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
637
+            } else {
638
+                // No rights
639
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
640
+            }
641
+        }
642
+
643
+        // Check if permitted to edit this field
644
+        if (!in_array($collectionName, $permittedFields)) {
645
+            throw new OCSException('', 103);
646
+        }
647
+
648
+        switch ($collectionName) {
649
+            case IAccountManager::COLLECTION_EMAIL:
650
+                $userAccount = $this->accountManager->getAccount($targetUser);
651
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
652
+                $mailCollection->removePropertyByValue($key);
653
+                if ($value !== '') {
654
+                    $mailCollection->addPropertyWithDefaults($value);
655
+                }
656
+                $this->accountManager->updateAccount($userAccount);
657
+                break;
658
+
659
+            case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
660
+                $userAccount = $this->accountManager->getAccount($targetUser);
661
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
662
+                $targetProperty = null;
663
+                foreach ($mailCollection->getProperties() as $property) {
664
+                    if ($property->getValue() === $key) {
665
+                        $targetProperty = $property;
666
+                        break;
667
+                    }
668
+                }
669
+                if ($targetProperty instanceof IAccountProperty) {
670
+                    try {
671
+                        $targetProperty->setScope($value);
672
+                        $this->accountManager->updateAccount($userAccount);
673
+                    } catch (\InvalidArgumentException $e) {
674
+                        throw new OCSException('', 102);
675
+                    }
676
+                } else {
677
+                    throw new OCSException('', 102);
678
+                }
679
+                break;
680
+
681
+            default:
682
+                throw new OCSException('', 103);
683
+        }
684
+        return new DataResponse();
685
+    }
686
+
687
+    /**
688
+     * @NoAdminRequired
689
+     * @NoSubAdminRequired
690
+     * @PasswordConfirmationRequired
691
+     *
692
+     * edit users
693
+     *
694
+     * @param string $userId
695
+     * @param string $key
696
+     * @param string $value
697
+     * @return DataResponse
698
+     * @throws OCSException
699
+     */
700
+    public function editUser(string $userId, string $key, string $value): DataResponse {
701
+        $currentLoggedInUser = $this->userSession->getUser();
702
+
703
+        $targetUser = $this->userManager->get($userId);
704
+        if ($targetUser === null) {
705
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
706
+        }
707
+
708
+        $permittedFields = [];
709
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
710
+            // Editing self (display, email)
711
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
712
+                if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
713
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
714
+                    $permittedFields[] = 'display';
715
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
716
+                }
717
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
718
+            }
719
+
720
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
721
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
722
+
723
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
724
+
725
+            $permittedFields[] = 'password';
726
+            if ($this->config->getSystemValue('force_language', false) === false ||
727
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
728
+                $permittedFields[] = 'language';
729
+            }
730
+
731
+            if ($this->config->getSystemValue('force_locale', false) === false ||
732
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
733
+                $permittedFields[] = 'locale';
734
+            }
735
+
736
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
737
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
738
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
739
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
740
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
741
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
742
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
743
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
744
+
745
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
746
+
747
+            // If admin they can edit their own quota
748
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
749
+                $permittedFields[] = 'quota';
750
+            }
751
+        } else {
752
+            // Check if admin / subadmin
753
+            $subAdminManager = $this->groupManager->getSubAdmin();
754
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
755
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
756
+                // They have permissions over the user
757
+                if ($targetUser->getBackend() instanceof ISetDisplayNameBackend
758
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)) {
759
+                    $permittedFields[] = 'display';
760
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
761
+                }
762
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
763
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
764
+                $permittedFields[] = 'password';
765
+                $permittedFields[] = 'language';
766
+                $permittedFields[] = 'locale';
767
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
768
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
769
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
770
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
771
+                $permittedFields[] = 'quota';
772
+            } else {
773
+                // No rights
774
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
775
+            }
776
+        }
777
+        // Check if permitted to edit this field
778
+        if (!in_array($key, $permittedFields)) {
779
+            throw new OCSException('', 103);
780
+        }
781
+        // Process the edit
782
+        switch ($key) {
783
+            case 'display':
784
+            case IAccountManager::PROPERTY_DISPLAYNAME:
785
+                $targetUser->setDisplayName($value);
786
+                break;
787
+            case 'quota':
788
+                $quota = $value;
789
+                if ($quota !== 'none' && $quota !== 'default') {
790
+                    if (is_numeric($quota)) {
791
+                        $quota = (float) $quota;
792
+                    } else {
793
+                        $quota = \OCP\Util::computerFileSize($quota);
794
+                    }
795
+                    if ($quota === false) {
796
+                        throw new OCSException('Invalid quota value '.$value, 102);
797
+                    }
798
+                    if ($quota === -1) {
799
+                        $quota = 'none';
800
+                    } else {
801
+                        $quota = \OCP\Util::humanFileSize($quota);
802
+                    }
803
+                }
804
+                $targetUser->setQuota($quota);
805
+                break;
806
+            case 'password':
807
+                try {
808
+                    if (!$targetUser->canChangePassword()) {
809
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
810
+                    }
811
+                    $targetUser->setPassword($value);
812
+                } catch (HintException $e) { // password policy error
813
+                    throw new OCSException($e->getMessage(), 103);
814
+                }
815
+                break;
816
+            case 'language':
817
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
818
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
819
+                    throw new OCSException('Invalid language', 102);
820
+                }
821
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
822
+                break;
823
+            case 'locale':
824
+                if (!$this->l10nFactory->localeExists($value)) {
825
+                    throw new OCSException('Invalid locale', 102);
826
+                }
827
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
828
+                break;
829
+            case IAccountManager::PROPERTY_EMAIL:
830
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
831
+                    $targetUser->setEMailAddress($value);
832
+                } else {
833
+                    throw new OCSException('', 102);
834
+                }
835
+                break;
836
+            case IAccountManager::COLLECTION_EMAIL:
837
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getEMailAddress()) {
838
+                    $userAccount = $this->accountManager->getAccount($targetUser);
839
+                    $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
840
+                    foreach ($mailCollection->getProperties() as $property) {
841
+                        if ($property->getValue() === $value) {
842
+                            break;
843
+                        }
844
+                    }
845
+                    $mailCollection->addPropertyWithDefaults($value);
846
+                    $this->accountManager->updateAccount($userAccount);
847
+                } else {
848
+                    throw new OCSException('', 102);
849
+                }
850
+                break;
851
+            case IAccountManager::PROPERTY_PHONE:
852
+            case IAccountManager::PROPERTY_ADDRESS:
853
+            case IAccountManager::PROPERTY_WEBSITE:
854
+            case IAccountManager::PROPERTY_TWITTER:
855
+                $userAccount = $this->accountManager->getAccount($targetUser);
856
+                try {
857
+                    $userProperty = $userAccount->getProperty($key);
858
+                    if ($userProperty->getValue() !== $value) {
859
+                        try {
860
+                            $userProperty->setValue($value);
861
+                            if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
862
+                                $this->knownUserService->deleteByContactUserId($targetUser->getUID());
863
+                            }
864
+                        } catch (\InvalidArgumentException $e) {
865
+                            throw new OCSException('Invalid ' . $e->getMessage(), 102);
866
+                        }
867
+                    }
868
+                } catch (PropertyDoesNotExistException $e) {
869
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
870
+                }
871
+                $this->accountManager->updateAccount($userAccount);
872
+                break;
873
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
874
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
875
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
876
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
877
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
878
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
879
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
880
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
881
+                $userAccount = $this->accountManager->getAccount($targetUser);
882
+                $userProperty = $userAccount->getProperty($propertyName);
883
+                if ($userProperty->getScope() !== $value) {
884
+                    try {
885
+                        $userProperty->setScope($value);
886
+                        $this->accountManager->updateAccount($userAccount);
887
+                    } catch (\InvalidArgumentException $e) {
888
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
889
+                    }
890
+                }
891
+                break;
892
+            default:
893
+                throw new OCSException('', 103);
894
+        }
895
+        return new DataResponse();
896
+    }
897
+
898
+    /**
899
+     * @PasswordConfirmationRequired
900
+     * @NoAdminRequired
901
+     *
902
+     * @param string $userId
903
+     *
904
+     * @return DataResponse
905
+     *
906
+     * @throws OCSException
907
+     */
908
+    public function wipeUserDevices(string $userId): DataResponse {
909
+        /** @var IUser $currentLoggedInUser */
910
+        $currentLoggedInUser = $this->userSession->getUser();
911
+
912
+        $targetUser = $this->userManager->get($userId);
913
+
914
+        if ($targetUser === null) {
915
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
916
+        }
917
+
918
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
919
+            throw new OCSException('', 101);
920
+        }
921
+
922
+        // If not permitted
923
+        $subAdminManager = $this->groupManager->getSubAdmin();
924
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
925
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
926
+        }
927
+
928
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
929
+
930
+        return new DataResponse();
931
+    }
932
+
933
+    /**
934
+     * @PasswordConfirmationRequired
935
+     * @NoAdminRequired
936
+     *
937
+     * @param string $userId
938
+     * @return DataResponse
939
+     * @throws OCSException
940
+     */
941
+    public function deleteUser(string $userId): DataResponse {
942
+        $currentLoggedInUser = $this->userSession->getUser();
943
+
944
+        $targetUser = $this->userManager->get($userId);
945
+
946
+        if ($targetUser === null) {
947
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
948
+        }
949
+
950
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
951
+            throw new OCSException('', 101);
952
+        }
953
+
954
+        // If not permitted
955
+        $subAdminManager = $this->groupManager->getSubAdmin();
956
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
957
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
958
+        }
959
+
960
+        // Go ahead with the delete
961
+        if ($targetUser->delete()) {
962
+            return new DataResponse();
963
+        } else {
964
+            throw new OCSException('', 101);
965
+        }
966
+    }
967
+
968
+    /**
969
+     * @PasswordConfirmationRequired
970
+     * @NoAdminRequired
971
+     *
972
+     * @param string $userId
973
+     * @return DataResponse
974
+     * @throws OCSException
975
+     * @throws OCSForbiddenException
976
+     */
977
+    public function disableUser(string $userId): DataResponse {
978
+        return $this->setEnabled($userId, false);
979
+    }
980
+
981
+    /**
982
+     * @PasswordConfirmationRequired
983
+     * @NoAdminRequired
984
+     *
985
+     * @param string $userId
986
+     * @return DataResponse
987
+     * @throws OCSException
988
+     * @throws OCSForbiddenException
989
+     */
990
+    public function enableUser(string $userId): DataResponse {
991
+        return $this->setEnabled($userId, true);
992
+    }
993
+
994
+    /**
995
+     * @param string $userId
996
+     * @param bool $value
997
+     * @return DataResponse
998
+     * @throws OCSException
999
+     */
1000
+    private function setEnabled(string $userId, bool $value): DataResponse {
1001
+        $currentLoggedInUser = $this->userSession->getUser();
1002
+
1003
+        $targetUser = $this->userManager->get($userId);
1004
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1005
+            throw new OCSException('', 101);
1006
+        }
1007
+
1008
+        // If not permitted
1009
+        $subAdminManager = $this->groupManager->getSubAdmin();
1010
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1011
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1012
+        }
1013
+
1014
+        // enable/disable the user now
1015
+        $targetUser->setEnabled($value);
1016
+        return new DataResponse();
1017
+    }
1018
+
1019
+    /**
1020
+     * @NoAdminRequired
1021
+     * @NoSubAdminRequired
1022
+     *
1023
+     * @param string $userId
1024
+     * @return DataResponse
1025
+     * @throws OCSException
1026
+     */
1027
+    public function getUsersGroups(string $userId): DataResponse {
1028
+        $loggedInUser = $this->userSession->getUser();
1029
+
1030
+        $targetUser = $this->userManager->get($userId);
1031
+        if ($targetUser === null) {
1032
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1033
+        }
1034
+
1035
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
1036
+            // Self lookup or admin lookup
1037
+            return new DataResponse([
1038
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
1039
+            ]);
1040
+        } else {
1041
+            $subAdminManager = $this->groupManager->getSubAdmin();
1042
+
1043
+            // Looking up someone else
1044
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1045
+                // Return the group that the method caller is subadmin of for the user in question
1046
+                /** @var IGroup[] $getSubAdminsGroups */
1047
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1048
+                foreach ($getSubAdminsGroups as $key => $group) {
1049
+                    $getSubAdminsGroups[$key] = $group->getGID();
1050
+                }
1051
+                $groups = array_intersect(
1052
+                    $getSubAdminsGroups,
1053
+                    $this->groupManager->getUserGroupIds($targetUser)
1054
+                );
1055
+                return new DataResponse(['groups' => $groups]);
1056
+            } else {
1057
+                // Not permitted
1058
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1059
+            }
1060
+        }
1061
+    }
1062
+
1063
+    /**
1064
+     * @PasswordConfirmationRequired
1065
+     * @NoAdminRequired
1066
+     *
1067
+     * @param string $userId
1068
+     * @param string $groupid
1069
+     * @return DataResponse
1070
+     * @throws OCSException
1071
+     */
1072
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1073
+        if ($groupid === '') {
1074
+            throw new OCSException('', 101);
1075
+        }
1076
+
1077
+        $group = $this->groupManager->get($groupid);
1078
+        $targetUser = $this->userManager->get($userId);
1079
+        if ($group === null) {
1080
+            throw new OCSException('', 102);
1081
+        }
1082
+        if ($targetUser === null) {
1083
+            throw new OCSException('', 103);
1084
+        }
1085
+
1086
+        // If they're not an admin, check they are a subadmin of the group in question
1087
+        $loggedInUser = $this->userSession->getUser();
1088
+        $subAdminManager = $this->groupManager->getSubAdmin();
1089
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1090
+            throw new OCSException('', 104);
1091
+        }
1092
+
1093
+        // Add user to group
1094
+        $group->addUser($targetUser);
1095
+        return new DataResponse();
1096
+    }
1097
+
1098
+    /**
1099
+     * @PasswordConfirmationRequired
1100
+     * @NoAdminRequired
1101
+     *
1102
+     * @param string $userId
1103
+     * @param string $groupid
1104
+     * @return DataResponse
1105
+     * @throws OCSException
1106
+     */
1107
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
1108
+        $loggedInUser = $this->userSession->getUser();
1109
+
1110
+        if ($groupid === null || trim($groupid) === '') {
1111
+            throw new OCSException('', 101);
1112
+        }
1113
+
1114
+        $group = $this->groupManager->get($groupid);
1115
+        if ($group === null) {
1116
+            throw new OCSException('', 102);
1117
+        }
1118
+
1119
+        $targetUser = $this->userManager->get($userId);
1120
+        if ($targetUser === null) {
1121
+            throw new OCSException('', 103);
1122
+        }
1123
+
1124
+        // If they're not an admin, check they are a subadmin of the group in question
1125
+        $subAdminManager = $this->groupManager->getSubAdmin();
1126
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1127
+            throw new OCSException('', 104);
1128
+        }
1129
+
1130
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
1131
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
1132
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
1133
+                if ($group->getGID() === 'admin') {
1134
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
1135
+                }
1136
+            } else {
1137
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1138
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
1139
+            }
1140
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1141
+            /** @var IGroup[] $subAdminGroups */
1142
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1143
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1144
+                return $subAdminGroup->getGID();
1145
+            }, $subAdminGroups);
1146
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
1147
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1148
+
1149
+            if (count($userSubAdminGroups) <= 1) {
1150
+                // Subadmin must not be able to remove a user from all their subadmin groups.
1151
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
1152
+            }
1153
+        }
1154
+
1155
+        // Remove user from group
1156
+        $group->removeUser($targetUser);
1157
+        return new DataResponse();
1158
+    }
1159
+
1160
+    /**
1161
+     * Creates a subadmin
1162
+     *
1163
+     * @PasswordConfirmationRequired
1164
+     *
1165
+     * @param string $userId
1166
+     * @param string $groupid
1167
+     * @return DataResponse
1168
+     * @throws OCSException
1169
+     */
1170
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
1171
+        $group = $this->groupManager->get($groupid);
1172
+        $user = $this->userManager->get($userId);
1173
+
1174
+        // Check if the user exists
1175
+        if ($user === null) {
1176
+            throw new OCSException('User does not exist', 101);
1177
+        }
1178
+        // Check if group exists
1179
+        if ($group === null) {
1180
+            throw new OCSException('Group does not exist',  102);
1181
+        }
1182
+        // Check if trying to make subadmin of admin group
1183
+        if ($group->getGID() === 'admin') {
1184
+            throw new OCSException('Cannot create subadmins for admin group', 103);
1185
+        }
1186
+
1187
+        $subAdminManager = $this->groupManager->getSubAdmin();
1188
+
1189
+        // We cannot be subadmin twice
1190
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1191
+            return new DataResponse();
1192
+        }
1193
+        // Go
1194
+        $subAdminManager->createSubAdmin($user, $group);
1195
+        return new DataResponse();
1196
+    }
1197
+
1198
+    /**
1199
+     * Removes a subadmin from a group
1200
+     *
1201
+     * @PasswordConfirmationRequired
1202
+     *
1203
+     * @param string $userId
1204
+     * @param string $groupid
1205
+     * @return DataResponse
1206
+     * @throws OCSException
1207
+     */
1208
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1209
+        $group = $this->groupManager->get($groupid);
1210
+        $user = $this->userManager->get($userId);
1211
+        $subAdminManager = $this->groupManager->getSubAdmin();
1212
+
1213
+        // Check if the user exists
1214
+        if ($user === null) {
1215
+            throw new OCSException('User does not exist', 101);
1216
+        }
1217
+        // Check if the group exists
1218
+        if ($group === null) {
1219
+            throw new OCSException('Group does not exist', 101);
1220
+        }
1221
+        // Check if they are a subadmin of this said group
1222
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1223
+            throw new OCSException('User is not a subadmin of this group', 102);
1224
+        }
1225
+
1226
+        // Go
1227
+        $subAdminManager->deleteSubAdmin($user, $group);
1228
+        return new DataResponse();
1229
+    }
1230
+
1231
+    /**
1232
+     * Get the groups a user is a subadmin of
1233
+     *
1234
+     * @param string $userId
1235
+     * @return DataResponse
1236
+     * @throws OCSException
1237
+     */
1238
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1239
+        $groups = $this->getUserSubAdminGroupsData($userId);
1240
+        return new DataResponse($groups);
1241
+    }
1242
+
1243
+    /**
1244
+     * @NoAdminRequired
1245
+     * @PasswordConfirmationRequired
1246
+     *
1247
+     * resend welcome message
1248
+     *
1249
+     * @param string $userId
1250
+     * @return DataResponse
1251
+     * @throws OCSException
1252
+     */
1253
+    public function resendWelcomeMessage(string $userId): DataResponse {
1254
+        $currentLoggedInUser = $this->userSession->getUser();
1255
+
1256
+        $targetUser = $this->userManager->get($userId);
1257
+        if ($targetUser === null) {
1258
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1259
+        }
1260
+
1261
+        // Check if admin / subadmin
1262
+        $subAdminManager = $this->groupManager->getSubAdmin();
1263
+        if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1264
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1265
+            // No rights
1266
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1267
+        }
1268
+
1269
+        $email = $targetUser->getEMailAddress();
1270
+        if ($email === '' || $email === null) {
1271
+            throw new OCSException('Email address not available', 101);
1272
+        }
1273
+
1274
+        try {
1275
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1276
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1277
+        } catch (\Exception $e) {
1278
+            $this->logger->error("Can't send new user mail to $email",
1279
+                [
1280
+                    'app' => 'settings',
1281
+                    'exception' => $e,
1282
+                ]
1283
+            );
1284
+            throw new OCSException('Sending email failed', 102);
1285
+        }
1286
+
1287
+        return new DataResponse();
1288
+    }
1289 1289
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
 		$matches = [];
288 288
 		foreach ($userMatches as $phone => $userId) {
289 289
 			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
290
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
290
+			$matches[$normalizedNumberToKey[$phone]] = $userId.'@'.$cloudUrl;
291 291
 			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
292 292
 		}
293 293
 
@@ -351,7 +351,7 @@  discard block
 block discarded – undo
351 351
 					throw new OCSException('group '.$group.' does not exist', 104);
352 352
 				}
353 353
 				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
354
-					throw new OCSException('insufficient privileges for group '. $group, 105);
354
+					throw new OCSException('insufficient privileges for group '.$group, 105);
355 355
 				}
356 356
 			}
357 357
 		} else {
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
 				$group = $this->groupManager->get($groupid);
367 367
 				// Check if group exists
368 368
 				if ($group === null) {
369
-					throw new OCSException('Subadmin group does not exist',  102);
369
+					throw new OCSException('Subadmin group does not exist', 102);
370 370
 				}
371 371
 				// Check if trying to make subadmin of admin group
372 372
 				if ($group->getGID() === 'admin') {
@@ -407,11 +407,11 @@  discard block
 block discarded – undo
407 407
 
408 408
 		try {
409 409
 			$newUser = $this->userManager->createUser($userid, $password);
410
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
410
+			$this->logger->info('Successful addUser call with userid: '.$userid, ['app' => 'ocs_api']);
411 411
 
412 412
 			foreach ($groups as $group) {
413 413
 				$this->groupManager->get($group)->addUser($newUser);
414
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
414
+				$this->logger->info('Added userid '.$userid.' to group '.$group, ['app' => 'ocs_api']);
415 415
 			}
416 416
 			foreach ($subadminGroups as $group) {
417 417
 				$subAdminManager->createSubAdmin($newUser, $group);
@@ -625,7 +625,7 @@  discard block
 block discarded – undo
625 625
 		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
626 626
 			// Editing self (display, email)
627 627
 			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
628
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
628
+			$permittedFields[] = IAccountManager::COLLECTION_EMAIL.self::SCOPE_SUFFIX;
629 629
 		} else {
630 630
 			// Check if admin / subadmin
631 631
 			$subAdminManager = $this->groupManager->getSubAdmin();
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
 				$this->accountManager->updateAccount($userAccount);
657 657
 				break;
658 658
 
659
-			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
659
+			case IAccountManager::COLLECTION_EMAIL.self::SCOPE_SUFFIX:
660 660
 				$userAccount = $this->accountManager->getAccount($targetUser);
661 661
 				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
662 662
 				$targetProperty = null;
@@ -717,8 +717,8 @@  discard block
 block discarded – undo
717 717
 				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
718 718
 			}
719 719
 
720
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
721
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
720
+			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME.self::SCOPE_SUFFIX;
721
+			$permittedFields[] = IAccountManager::PROPERTY_EMAIL.self::SCOPE_SUFFIX;
722 722
 
723 723
 			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
724 724
 
@@ -737,12 +737,12 @@  discard block
 block discarded – undo
737 737
 			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
738 738
 			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
739 739
 			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
740
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
741
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
742
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
743
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
740
+			$permittedFields[] = IAccountManager::PROPERTY_PHONE.self::SCOPE_SUFFIX;
741
+			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS.self::SCOPE_SUFFIX;
742
+			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE.self::SCOPE_SUFFIX;
743
+			$permittedFields[] = IAccountManager::PROPERTY_TWITTER.self::SCOPE_SUFFIX;
744 744
 
745
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
745
+			$permittedFields[] = IAccountManager::PROPERTY_AVATAR.self::SCOPE_SUFFIX;
746 746
 
747 747
 			// If admin they can edit their own quota
748 748
 			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
@@ -862,7 +862,7 @@  discard block
 block discarded – undo
862 862
 								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
863 863
 							}
864 864
 						} catch (\InvalidArgumentException $e) {
865
-							throw new OCSException('Invalid ' . $e->getMessage(), 102);
865
+							throw new OCSException('Invalid '.$e->getMessage(), 102);
866 866
 						}
867 867
 					}
868 868
 				} catch (PropertyDoesNotExistException $e) {
@@ -870,13 +870,13 @@  discard block
 block discarded – undo
870 870
 				}
871 871
 				$this->accountManager->updateAccount($userAccount);
872 872
 				break;
873
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
874
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
875
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
876
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
877
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
878
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
879
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
873
+			case IAccountManager::PROPERTY_DISPLAYNAME.self::SCOPE_SUFFIX:
874
+			case IAccountManager::PROPERTY_EMAIL.self::SCOPE_SUFFIX:
875
+			case IAccountManager::PROPERTY_PHONE.self::SCOPE_SUFFIX:
876
+			case IAccountManager::PROPERTY_ADDRESS.self::SCOPE_SUFFIX:
877
+			case IAccountManager::PROPERTY_WEBSITE.self::SCOPE_SUFFIX:
878
+			case IAccountManager::PROPERTY_TWITTER.self::SCOPE_SUFFIX:
879
+			case IAccountManager::PROPERTY_AVATAR.self::SCOPE_SUFFIX:
880 880
 				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
881 881
 				$userAccount = $this->accountManager->getAccount($targetUser);
882 882
 				$userProperty = $userAccount->getProperty($propertyName);
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
 						$userProperty->setScope($value);
886 886
 						$this->accountManager->updateAccount($userAccount);
887 887
 					} catch (\InvalidArgumentException $e) {
888
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
888
+						throw new OCSException('Invalid '.$e->getMessage(), 102);
889 889
 					}
890 890
 				}
891 891
 				break;
@@ -1140,7 +1140,7 @@  discard block
 block discarded – undo
1140 1140
 		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
1141 1141
 			/** @var IGroup[] $subAdminGroups */
1142 1142
 			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1143
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1143
+			$subAdminGroups = array_map(function(IGroup $subAdminGroup) {
1144 1144
 				return $subAdminGroup->getGID();
1145 1145
 			}, $subAdminGroups);
1146 1146
 			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
@@ -1177,7 +1177,7 @@  discard block
 block discarded – undo
1177 1177
 		}
1178 1178
 		// Check if group exists
1179 1179
 		if ($group === null) {
1180
-			throw new OCSException('Group does not exist',  102);
1180
+			throw new OCSException('Group does not exist', 102);
1181 1181
 		}
1182 1182
 		// Check if trying to make subadmin of admin group
1183 1183
 		if ($group->getGID() === 'admin') {
Please login to merge, or discard this patch.