Passed
Push — master ( fda6ff...86a3b7 )
by Joas
16:22 queued 12s
created
core/Migrations/Version21000Date20201202095923.php 1 patch
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -32,44 +32,44 @@
 block discarded – undo
32 32
 use OCP\Migration\SimpleMigrationStep;
33 33
 
34 34
 class Version21000Date20201202095923 extends SimpleMigrationStep {
35
-	/**
36
-	 * @param IOutput $output
37
-	 * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
38
-	 * @param array $options
39
-	 * @return null|ISchemaWrapper
40
-	 */
41
-	public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
42
-		/** @var ISchemaWrapper $schema */
43
-		$schema = $schemaClosure();
35
+    /**
36
+     * @param IOutput $output
37
+     * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
38
+     * @param array $options
39
+     * @return null|ISchemaWrapper
40
+     */
41
+    public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
42
+        /** @var ISchemaWrapper $schema */
43
+        $schema = $schemaClosure();
44 44
 
45
-		if (!$schema->hasTable('accounts_data')) {
46
-			$table = $schema->createTable('accounts_data');
47
-			$table->addColumn('id', Types::BIGINT, [
48
-				'autoincrement' => true,
49
-				'notnull' => true,
50
-				'length' => 20,
51
-			]);
52
-			$table->addColumn('uid', Types::STRING, [
53
-				'notnull' => true,
54
-				'length' => 64,
55
-			]);
56
-			$table->addColumn('name', Types::STRING, [
57
-				'notnull' => true,
58
-				'length' => 64,
59
-			]);
60
-			$table->addColumn('value', Types::STRING, [
61
-				'notnull' => false,
62
-				'length' => 255,
63
-				'default' => '',
64
-			]);
65
-			$table->setPrimaryKey(['id']);
66
-			$table->addIndex(['uid'], 'accounts_data_uid');
67
-			$table->addIndex(['name'], 'accounts_data_name');
68
-			$table->addIndex(['value'], 'accounts_data_value');
45
+        if (!$schema->hasTable('accounts_data')) {
46
+            $table = $schema->createTable('accounts_data');
47
+            $table->addColumn('id', Types::BIGINT, [
48
+                'autoincrement' => true,
49
+                'notnull' => true,
50
+                'length' => 20,
51
+            ]);
52
+            $table->addColumn('uid', Types::STRING, [
53
+                'notnull' => true,
54
+                'length' => 64,
55
+            ]);
56
+            $table->addColumn('name', Types::STRING, [
57
+                'notnull' => true,
58
+                'length' => 64,
59
+            ]);
60
+            $table->addColumn('value', Types::STRING, [
61
+                'notnull' => false,
62
+                'length' => 255,
63
+                'default' => '',
64
+            ]);
65
+            $table->setPrimaryKey(['id']);
66
+            $table->addIndex(['uid'], 'accounts_data_uid');
67
+            $table->addIndex(['name'], 'accounts_data_name');
68
+            $table->addIndex(['value'], 'accounts_data_value');
69 69
 
70
-			return $schema;
71
-		}
70
+            return $schema;
71
+        }
72 72
 
73
-		return null;
74
-	}
73
+        return null;
74
+    }
75 75
 }
Please login to merge, or discard this patch.
apps/dav/lib/CardDAV/Converter.php 1 patch
Indentation   +115 added lines, -115 removed lines patch added patch discarded remove patch
@@ -34,119 +34,119 @@
 block discarded – undo
34 34
 
35 35
 class Converter {
36 36
 
37
-	/** @var AccountManager */
38
-	private $accountManager;
39
-
40
-	/**
41
-	 * Converter constructor.
42
-	 *
43
-	 * @param AccountManager $accountManager
44
-	 */
45
-	public function __construct(AccountManager $accountManager) {
46
-		$this->accountManager = $accountManager;
47
-	}
48
-
49
-	/**
50
-	 * @param IUser $user
51
-	 * @return VCard|null
52
-	 */
53
-	public function createCardFromUser(IUser $user) {
54
-		$userData = $this->accountManager->getUser($user);
55
-
56
-		$uid = $user->getUID();
57
-		$cloudId = $user->getCloudId();
58
-		$image = $this->getAvatarImage($user);
59
-
60
-		$vCard = new VCard();
61
-		$vCard->VERSION = '3.0';
62
-		$vCard->UID = $uid;
63
-
64
-		$publish = false;
65
-
66
-		if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
67
-			$userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
68
-		}
69
-
70
-		foreach ($userData as $property => $value) {
71
-			$shareWithTrustedServers =
72
-				$value['scope'] === AccountManager::VISIBILITY_CONTACTS_ONLY ||
73
-				$value['scope'] === AccountManager::VISIBILITY_PUBLIC;
74
-
75
-			$emptyValue = !isset($value['value']) || $value['value'] === '';
76
-
77
-			if ($shareWithTrustedServers && !$emptyValue) {
78
-				$publish = true;
79
-				switch ($property) {
80
-					case IAccountManager::PROPERTY_DISPLAYNAME:
81
-						$vCard->add(new Text($vCard, 'FN', $value['value']));
82
-						$vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
83
-						break;
84
-					case IAccountManager::PROPERTY_AVATAR:
85
-						if ($image !== null) {
86
-							$vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
87
-						}
88
-						break;
89
-					case IAccountManager::PROPERTY_EMAIL:
90
-						$vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
91
-						break;
92
-					case IAccountManager::PROPERTY_WEBSITE:
93
-						$vCard->add(new Text($vCard, 'URL', $value['value']));
94
-						break;
95
-					case IAccountManager::PROPERTY_PHONE:
96
-						$vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
97
-						break;
98
-					case IAccountManager::PROPERTY_ADDRESS:
99
-						$vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
100
-						break;
101
-					case IAccountManager::PROPERTY_TWITTER:
102
-						$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
103
-						break;
104
-				}
105
-			}
106
-		}
107
-
108
-		if ($publish && !empty($cloudId)) {
109
-			$vCard->add(new Text($vCard, 'CLOUD', $cloudId));
110
-			$vCard->validate();
111
-			return $vCard;
112
-		}
113
-
114
-		return null;
115
-	}
116
-
117
-	/**
118
-	 * @param string $fullName
119
-	 * @return string[]
120
-	 */
121
-	public function splitFullName($fullName) {
122
-		// Very basic western style parsing. I'm not gonna implement
123
-		// https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
124
-
125
-		$elements = explode(' ', $fullName);
126
-		$result = ['', '', '', '', ''];
127
-		if (count($elements) > 2) {
128
-			$result[0] = implode(' ', array_slice($elements, count($elements) - 1));
129
-			$result[1] = $elements[0];
130
-			$result[2] = implode(' ', array_slice($elements, 1, count($elements) - 2));
131
-		} elseif (count($elements) === 2) {
132
-			$result[0] = $elements[1];
133
-			$result[1] = $elements[0];
134
-		} else {
135
-			$result[0] = $elements[0];
136
-		}
137
-
138
-		return $result;
139
-	}
140
-
141
-	/**
142
-	 * @param IUser $user
143
-	 * @return null|IImage
144
-	 */
145
-	private function getAvatarImage(IUser $user) {
146
-		try {
147
-			return $user->getAvatarImage(-1);
148
-		} catch (\Exception $ex) {
149
-			return null;
150
-		}
151
-	}
37
+    /** @var AccountManager */
38
+    private $accountManager;
39
+
40
+    /**
41
+     * Converter constructor.
42
+     *
43
+     * @param AccountManager $accountManager
44
+     */
45
+    public function __construct(AccountManager $accountManager) {
46
+        $this->accountManager = $accountManager;
47
+    }
48
+
49
+    /**
50
+     * @param IUser $user
51
+     * @return VCard|null
52
+     */
53
+    public function createCardFromUser(IUser $user) {
54
+        $userData = $this->accountManager->getUser($user);
55
+
56
+        $uid = $user->getUID();
57
+        $cloudId = $user->getCloudId();
58
+        $image = $this->getAvatarImage($user);
59
+
60
+        $vCard = new VCard();
61
+        $vCard->VERSION = '3.0';
62
+        $vCard->UID = $uid;
63
+
64
+        $publish = false;
65
+
66
+        if ($image !== null && isset($userData[IAccountManager::PROPERTY_AVATAR])) {
67
+            $userData[IAccountManager::PROPERTY_AVATAR]['value'] = true;
68
+        }
69
+
70
+        foreach ($userData as $property => $value) {
71
+            $shareWithTrustedServers =
72
+                $value['scope'] === AccountManager::VISIBILITY_CONTACTS_ONLY ||
73
+                $value['scope'] === AccountManager::VISIBILITY_PUBLIC;
74
+
75
+            $emptyValue = !isset($value['value']) || $value['value'] === '';
76
+
77
+            if ($shareWithTrustedServers && !$emptyValue) {
78
+                $publish = true;
79
+                switch ($property) {
80
+                    case IAccountManager::PROPERTY_DISPLAYNAME:
81
+                        $vCard->add(new Text($vCard, 'FN', $value['value']));
82
+                        $vCard->add(new Text($vCard, 'N', $this->splitFullName($value['value'])));
83
+                        break;
84
+                    case IAccountManager::PROPERTY_AVATAR:
85
+                        if ($image !== null) {
86
+                            $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
87
+                        }
88
+                        break;
89
+                    case IAccountManager::PROPERTY_EMAIL:
90
+                        $vCard->add(new Text($vCard, 'EMAIL', $value['value'], ['TYPE' => 'OTHER']));
91
+                        break;
92
+                    case IAccountManager::PROPERTY_WEBSITE:
93
+                        $vCard->add(new Text($vCard, 'URL', $value['value']));
94
+                        break;
95
+                    case IAccountManager::PROPERTY_PHONE:
96
+                        $vCard->add(new Text($vCard, 'TEL', $value['value'], ['TYPE' => 'OTHER']));
97
+                        break;
98
+                    case IAccountManager::PROPERTY_ADDRESS:
99
+                        $vCard->add(new Text($vCard, 'ADR', $value['value'], ['TYPE' => 'OTHER']));
100
+                        break;
101
+                    case IAccountManager::PROPERTY_TWITTER:
102
+                        $vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $value['value'], ['TYPE' => 'TWITTER']));
103
+                        break;
104
+                }
105
+            }
106
+        }
107
+
108
+        if ($publish && !empty($cloudId)) {
109
+            $vCard->add(new Text($vCard, 'CLOUD', $cloudId));
110
+            $vCard->validate();
111
+            return $vCard;
112
+        }
113
+
114
+        return null;
115
+    }
116
+
117
+    /**
118
+     * @param string $fullName
119
+     * @return string[]
120
+     */
121
+    public function splitFullName($fullName) {
122
+        // Very basic western style parsing. I'm not gonna implement
123
+        // https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
124
+
125
+        $elements = explode(' ', $fullName);
126
+        $result = ['', '', '', '', ''];
127
+        if (count($elements) > 2) {
128
+            $result[0] = implode(' ', array_slice($elements, count($elements) - 1));
129
+            $result[1] = $elements[0];
130
+            $result[2] = implode(' ', array_slice($elements, 1, count($elements) - 2));
131
+        } elseif (count($elements) === 2) {
132
+            $result[0] = $elements[1];
133
+            $result[1] = $elements[0];
134
+        } else {
135
+            $result[0] = $elements[0];
136
+        }
137
+
138
+        return $result;
139
+    }
140
+
141
+    /**
142
+     * @param IUser $user
143
+     * @return null|IImage
144
+     */
145
+    private function getAvatarImage(IUser $user) {
146
+        try {
147
+            return $user->getAvatarImage(-1);
148
+        } catch (\Exception $ex) {
149
+            return null;
150
+        }
151
+    }
152 152
 }
Please login to merge, or discard this patch.
apps/provisioning_api/appinfo/routes.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -27,50 +27,50 @@
 block discarded – undo
27 27
  */
28 28
 
29 29
 return [
30
-	'ocs' => [
31
-		// Apps
32
-		['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
33
-		['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
34
-		['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
35
-		['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
30
+    'ocs' => [
31
+        // Apps
32
+        ['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
33
+        ['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
34
+        ['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
35
+        ['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
36 36
 
37
-		// Groups
38
-		['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
39
-		['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
40
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
41
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
42
-		['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
43
-		['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
44
-		['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
45
-		['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
46
-		['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
37
+        // Groups
38
+        ['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
39
+        ['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
40
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
41
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
42
+        ['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
43
+        ['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
44
+        ['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
45
+        ['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
46
+        ['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
47 47
 
48
-		// Users
49
-		['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
50
-		['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
51
-		['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
52
-		['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
53
-		['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
54
-		['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
55
-		['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
56
-		['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
57
-		['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
58
-		['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
59
-		['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
60
-		['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
61
-		['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
62
-		['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
63
-		['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
64
-		['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
65
-		['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
66
-		['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
67
-		['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
48
+        // Users
49
+        ['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
50
+        ['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
51
+        ['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
52
+        ['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
53
+        ['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
54
+        ['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
55
+        ['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
56
+        ['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
57
+        ['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
58
+        ['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
59
+        ['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
60
+        ['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
61
+        ['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
62
+        ['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
63
+        ['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
64
+        ['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
65
+        ['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
66
+        ['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
67
+        ['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
68 68
 
69
-		// Config
70
-		['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
71
-		['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
72
-		['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
73
-		['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
74
-		['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
75
-	],
69
+        // Config
70
+        ['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
71
+        ['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
72
+        ['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
73
+        ['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
74
+        ['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
75
+    ],
76 76
 ];
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/AUserData.php 1 patch
Indentation   +162 added lines, -162 removed lines patch added patch discarded remove patch
@@ -51,166 +51,166 @@
 block discarded – undo
51 51
 
52 52
 abstract class AUserData extends OCSController {
53 53
 
54
-	/** @var IUserManager */
55
-	protected $userManager;
56
-	/** @var IConfig */
57
-	protected $config;
58
-	/** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
59
-	protected $groupManager;
60
-	/** @var IUserSession */
61
-	protected $userSession;
62
-	/** @var AccountManager */
63
-	protected $accountManager;
64
-	/** @var IFactory */
65
-	protected $l10nFactory;
66
-
67
-	public function __construct(string $appName,
68
-								IRequest $request,
69
-								IUserManager $userManager,
70
-								IConfig $config,
71
-								IGroupManager $groupManager,
72
-								IUserSession $userSession,
73
-								AccountManager $accountManager,
74
-								IFactory $l10nFactory) {
75
-		parent::__construct($appName, $request);
76
-
77
-		$this->userManager = $userManager;
78
-		$this->config = $config;
79
-		$this->groupManager = $groupManager;
80
-		$this->userSession = $userSession;
81
-		$this->accountManager = $accountManager;
82
-		$this->l10nFactory = $l10nFactory;
83
-	}
84
-
85
-	/**
86
-	 * creates a array with all user data
87
-	 *
88
-	 * @param string $userId
89
-	 * @return array
90
-	 * @throws NotFoundException
91
-	 * @throws OCSException
92
-	 * @throws OCSNotFoundException
93
-	 */
94
-	protected function getUserData(string $userId): array {
95
-		$currentLoggedInUser = $this->userSession->getUser();
96
-
97
-		$data = [];
98
-
99
-		// Check if the target user exists
100
-		$targetUserObject = $this->userManager->get($userId);
101
-		if ($targetUserObject === null) {
102
-			throw new OCSNotFoundException('User does not exist');
103
-		}
104
-
105
-		// Should be at least Admin Or SubAdmin!
106
-		if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
107
-			|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
108
-			$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true';
109
-		} else {
110
-			// Check they are looking up themselves
111
-			if ($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
112
-				return $data;
113
-			}
114
-		}
115
-
116
-		// Get groups data
117
-		$userAccount = $this->accountManager->getUser($targetUserObject);
118
-		$groups = $this->groupManager->getUserGroups($targetUserObject);
119
-		$gids = [];
120
-		foreach ($groups as $group) {
121
-			$gids[] = $group->getGID();
122
-		}
123
-
124
-		try {
125
-			# might be thrown by LDAP due to handling of users disappears
126
-			# from the external source (reasons unknown to us)
127
-			# cf. https://github.com/nextcloud/server/issues/12991
128
-			$data['storageLocation'] = $targetUserObject->getHome();
129
-		} catch (NoUserException $e) {
130
-			throw new OCSNotFoundException($e->getMessage(), $e);
131
-		}
132
-
133
-		// Find the data
134
-		$data['id'] = $targetUserObject->getUID();
135
-		$data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
136
-		$data['backend'] = $targetUserObject->getBackendClassName();
137
-		$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
138
-		$data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
139
-		$data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
140
-		$data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
141
-		$data[IAccountManager::PROPERTY_PHONE] = $userAccount[IAccountManager::PROPERTY_PHONE]['value'];
142
-		$data[IAccountManager::PROPERTY_ADDRESS] = $userAccount[IAccountManager::PROPERTY_ADDRESS]['value'];
143
-		$data[IAccountManager::PROPERTY_WEBSITE] = $userAccount[IAccountManager::PROPERTY_WEBSITE]['value'];
144
-		$data[IAccountManager::PROPERTY_TWITTER] = $userAccount[IAccountManager::PROPERTY_TWITTER]['value'];
145
-		$data['groups'] = $gids;
146
-		$data['language'] = $this->l10nFactory->getUserLanguage($targetUserObject);
147
-		$data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
148
-
149
-		$backend = $targetUserObject->getBackend();
150
-		$data['backendCapabilities'] = [
151
-			'setDisplayName' => $backend instanceof ISetDisplayNameBackend || $backend->implementsActions(Backend::SET_DISPLAYNAME),
152
-			'setPassword' => $backend instanceof ISetPasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD),
153
-		];
154
-
155
-		return $data;
156
-	}
157
-
158
-	/**
159
-	 * Get the groups a user is a subadmin of
160
-	 *
161
-	 * @param string $userId
162
-	 * @return array
163
-	 * @throws OCSException
164
-	 */
165
-	protected function getUserSubAdminGroupsData(string $userId): array {
166
-		$user = $this->userManager->get($userId);
167
-		// Check if the user exists
168
-		if ($user === null) {
169
-			throw new OCSNotFoundException('User does not exist');
170
-		}
171
-
172
-		// Get the subadmin groups
173
-		$subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
174
-		$groups = [];
175
-		foreach ($subAdminGroups as $key => $group) {
176
-			$groups[] = $group->getGID();
177
-		}
178
-
179
-		return $groups;
180
-	}
181
-
182
-	/**
183
-	 * @param string $userId
184
-	 * @return array
185
-	 * @throws \OCP\Files\NotFoundException
186
-	 */
187
-	protected function fillStorageInfo(string $userId): array {
188
-		try {
189
-			\OC_Util::tearDownFS();
190
-			\OC_Util::setupFS($userId);
191
-			$storage = OC_Helper::getStorageInfo('/');
192
-			$data = [
193
-				'free' => $storage['free'],
194
-				'used' => $storage['used'],
195
-				'total' => $storage['total'],
196
-				'relative' => $storage['relative'],
197
-				'quota' => $storage['quota'],
198
-			];
199
-		} catch (NotFoundException $ex) {
200
-			// User fs is not setup yet
201
-			$user = $this->userManager->get($userId);
202
-			if ($user === null) {
203
-				throw new OCSException('User does not exist', 101);
204
-			}
205
-			$quota = $user->getQuota();
206
-			if ($quota !== 'none') {
207
-				$quota = OC_Helper::computerFileSize($quota);
208
-			}
209
-			$data = [
210
-				'quota' => $quota !== false ? $quota : 'none',
211
-				'used' => 0
212
-			];
213
-		}
214
-		return $data;
215
-	}
54
+    /** @var IUserManager */
55
+    protected $userManager;
56
+    /** @var IConfig */
57
+    protected $config;
58
+    /** @var IGroupManager|\OC\Group\Manager */ // FIXME Requires a method that is not on the interface
59
+    protected $groupManager;
60
+    /** @var IUserSession */
61
+    protected $userSession;
62
+    /** @var AccountManager */
63
+    protected $accountManager;
64
+    /** @var IFactory */
65
+    protected $l10nFactory;
66
+
67
+    public function __construct(string $appName,
68
+                                IRequest $request,
69
+                                IUserManager $userManager,
70
+                                IConfig $config,
71
+                                IGroupManager $groupManager,
72
+                                IUserSession $userSession,
73
+                                AccountManager $accountManager,
74
+                                IFactory $l10nFactory) {
75
+        parent::__construct($appName, $request);
76
+
77
+        $this->userManager = $userManager;
78
+        $this->config = $config;
79
+        $this->groupManager = $groupManager;
80
+        $this->userSession = $userSession;
81
+        $this->accountManager = $accountManager;
82
+        $this->l10nFactory = $l10nFactory;
83
+    }
84
+
85
+    /**
86
+     * creates a array with all user data
87
+     *
88
+     * @param string $userId
89
+     * @return array
90
+     * @throws NotFoundException
91
+     * @throws OCSException
92
+     * @throws OCSNotFoundException
93
+     */
94
+    protected function getUserData(string $userId): array {
95
+        $currentLoggedInUser = $this->userSession->getUser();
96
+
97
+        $data = [];
98
+
99
+        // Check if the target user exists
100
+        $targetUserObject = $this->userManager->get($userId);
101
+        if ($targetUserObject === null) {
102
+            throw new OCSNotFoundException('User does not exist');
103
+        }
104
+
105
+        // Should be at least Admin Or SubAdmin!
106
+        if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
107
+            || $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
108
+            $data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true';
109
+        } else {
110
+            // Check they are looking up themselves
111
+            if ($currentLoggedInUser->getUID() !== $targetUserObject->getUID()) {
112
+                return $data;
113
+            }
114
+        }
115
+
116
+        // Get groups data
117
+        $userAccount = $this->accountManager->getUser($targetUserObject);
118
+        $groups = $this->groupManager->getUserGroups($targetUserObject);
119
+        $gids = [];
120
+        foreach ($groups as $group) {
121
+            $gids[] = $group->getGID();
122
+        }
123
+
124
+        try {
125
+            # might be thrown by LDAP due to handling of users disappears
126
+            # from the external source (reasons unknown to us)
127
+            # cf. https://github.com/nextcloud/server/issues/12991
128
+            $data['storageLocation'] = $targetUserObject->getHome();
129
+        } catch (NoUserException $e) {
130
+            throw new OCSNotFoundException($e->getMessage(), $e);
131
+        }
132
+
133
+        // Find the data
134
+        $data['id'] = $targetUserObject->getUID();
135
+        $data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
136
+        $data['backend'] = $targetUserObject->getBackendClassName();
137
+        $data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
138
+        $data['quota'] = $this->fillStorageInfo($targetUserObject->getUID());
139
+        $data[IAccountManager::PROPERTY_EMAIL] = $targetUserObject->getEMailAddress();
140
+        $data[IAccountManager::PROPERTY_DISPLAYNAME] = $targetUserObject->getDisplayName();
141
+        $data[IAccountManager::PROPERTY_PHONE] = $userAccount[IAccountManager::PROPERTY_PHONE]['value'];
142
+        $data[IAccountManager::PROPERTY_ADDRESS] = $userAccount[IAccountManager::PROPERTY_ADDRESS]['value'];
143
+        $data[IAccountManager::PROPERTY_WEBSITE] = $userAccount[IAccountManager::PROPERTY_WEBSITE]['value'];
144
+        $data[IAccountManager::PROPERTY_TWITTER] = $userAccount[IAccountManager::PROPERTY_TWITTER]['value'];
145
+        $data['groups'] = $gids;
146
+        $data['language'] = $this->l10nFactory->getUserLanguage($targetUserObject);
147
+        $data['locale'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'locale');
148
+
149
+        $backend = $targetUserObject->getBackend();
150
+        $data['backendCapabilities'] = [
151
+            'setDisplayName' => $backend instanceof ISetDisplayNameBackend || $backend->implementsActions(Backend::SET_DISPLAYNAME),
152
+            'setPassword' => $backend instanceof ISetPasswordBackend || $backend->implementsActions(Backend::SET_PASSWORD),
153
+        ];
154
+
155
+        return $data;
156
+    }
157
+
158
+    /**
159
+     * Get the groups a user is a subadmin of
160
+     *
161
+     * @param string $userId
162
+     * @return array
163
+     * @throws OCSException
164
+     */
165
+    protected function getUserSubAdminGroupsData(string $userId): array {
166
+        $user = $this->userManager->get($userId);
167
+        // Check if the user exists
168
+        if ($user === null) {
169
+            throw new OCSNotFoundException('User does not exist');
170
+        }
171
+
172
+        // Get the subadmin groups
173
+        $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($user);
174
+        $groups = [];
175
+        foreach ($subAdminGroups as $key => $group) {
176
+            $groups[] = $group->getGID();
177
+        }
178
+
179
+        return $groups;
180
+    }
181
+
182
+    /**
183
+     * @param string $userId
184
+     * @return array
185
+     * @throws \OCP\Files\NotFoundException
186
+     */
187
+    protected function fillStorageInfo(string $userId): array {
188
+        try {
189
+            \OC_Util::tearDownFS();
190
+            \OC_Util::setupFS($userId);
191
+            $storage = OC_Helper::getStorageInfo('/');
192
+            $data = [
193
+                'free' => $storage['free'],
194
+                'used' => $storage['used'],
195
+                'total' => $storage['total'],
196
+                'relative' => $storage['relative'],
197
+                'quota' => $storage['quota'],
198
+            ];
199
+        } catch (NotFoundException $ex) {
200
+            // User fs is not setup yet
201
+            $user = $this->userManager->get($userId);
202
+            if ($user === null) {
203
+                throw new OCSException('User does not exist', 101);
204
+            }
205
+            $quota = $user->getQuota();
206
+            if ($quota !== 'none') {
207
+                $quota = OC_Helper::computerFileSize($quota);
208
+            }
209
+            $data = [
210
+                'quota' => $quota !== false ? $quota : 'none',
211
+                'used' => 0
212
+            ];
213
+        }
214
+        return $data;
215
+    }
216 216
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 2 patches
Indentation   +984 added lines, -984 removed lines patch added patch discarded remove patch
@@ -72,988 +72,988 @@
 block discarded – undo
72 72
 
73 73
 class UsersController extends AUserData {
74 74
 
75
-	/** @var IAppManager */
76
-	private $appManager;
77
-	/** @var IURLGenerator */
78
-	protected $urlGenerator;
79
-	/** @var ILogger */
80
-	private $logger;
81
-	/** @var IFactory */
82
-	protected $l10nFactory;
83
-	/** @var NewUserMailHelper */
84
-	private $newUserMailHelper;
85
-	/** @var FederatedShareProviderFactory */
86
-	private $federatedShareProviderFactory;
87
-	/** @var ISecureRandom */
88
-	private $secureRandom;
89
-	/** @var RemoteWipe */
90
-	private $remoteWipe;
91
-	/** @var IEventDispatcher */
92
-	private $eventDispatcher;
93
-
94
-	public function __construct(string $appName,
95
-								IRequest $request,
96
-								IUserManager $userManager,
97
-								IConfig $config,
98
-								IAppManager $appManager,
99
-								IGroupManager $groupManager,
100
-								IUserSession $userSession,
101
-								AccountManager $accountManager,
102
-								IURLGenerator $urlGenerator,
103
-								ILogger $logger,
104
-								IFactory $l10nFactory,
105
-								NewUserMailHelper $newUserMailHelper,
106
-								FederatedShareProviderFactory $federatedShareProviderFactory,
107
-								ISecureRandom $secureRandom,
108
-								RemoteWipe $remoteWipe,
109
-								IEventDispatcher $eventDispatcher) {
110
-		parent::__construct($appName,
111
-							$request,
112
-							$userManager,
113
-							$config,
114
-							$groupManager,
115
-							$userSession,
116
-							$accountManager,
117
-							$l10nFactory);
118
-
119
-		$this->appManager = $appManager;
120
-		$this->urlGenerator = $urlGenerator;
121
-		$this->logger = $logger;
122
-		$this->l10nFactory = $l10nFactory;
123
-		$this->newUserMailHelper = $newUserMailHelper;
124
-		$this->federatedShareProviderFactory = $federatedShareProviderFactory;
125
-		$this->secureRandom = $secureRandom;
126
-		$this->remoteWipe = $remoteWipe;
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
-		$normalizedNumberToKey = [];
233
-		foreach ($search as $key => $phoneNumbers) {
234
-			foreach ($phoneNumbers as $phone) {
235
-				try {
236
-					$phoneNumber = $phoneUtil->parse($phone, $location);
237
-					if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
238
-						$normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
239
-						$normalizedNumberToKey[$normalizedNumber] = (string) $key;
240
-					}
241
-				} catch (NumberParseException $e) {
242
-				}
243
-			}
244
-		}
245
-
246
-		$phoneNumbers = array_keys($normalizedNumberToKey);
247
-
248
-		if (empty($phoneNumbers)) {
249
-			return new DataResponse();
250
-		}
251
-
252
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
253
-
254
-		if (empty($userMatches)) {
255
-			return new DataResponse();
256
-		}
257
-
258
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
259
-		if (strpos($cloudUrl, 'http://') === 0) {
260
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
261
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
262
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
263
-		}
264
-
265
-		$matches = [];
266
-		foreach ($userMatches as $phone => $userId) {
267
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
268
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
269
-		}
270
-
271
-		return new DataResponse($matches);
272
-	}
273
-
274
-	/**
275
-	 * @throws OCSException
276
-	 */
277
-	private function createNewUserId(): string {
278
-		$attempts = 0;
279
-		do {
280
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
281
-			if (!$this->userManager->userExists($uidCandidate)) {
282
-				return $uidCandidate;
283
-			}
284
-			$attempts++;
285
-		} while ($attempts < 10);
286
-		throw new OCSException('Could not create non-existing user id', 111);
287
-	}
288
-
289
-	/**
290
-	 * @PasswordConfirmationRequired
291
-	 * @NoAdminRequired
292
-	 *
293
-	 * @param string $userid
294
-	 * @param string $password
295
-	 * @param string $displayName
296
-	 * @param string $email
297
-	 * @param array $groups
298
-	 * @param array $subadmin
299
-	 * @param string $quota
300
-	 * @param string $language
301
-	 * @return DataResponse
302
-	 * @throws OCSException
303
-	 */
304
-	public function addUser(string $userid,
305
-							string $password = '',
306
-							string $displayName = '',
307
-							string $email = '',
308
-							array $groups = [],
309
-							array $subadmin = [],
310
-							string $quota = '',
311
-							string $language = ''): DataResponse {
312
-		$user = $this->userSession->getUser();
313
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
314
-		$subAdminManager = $this->groupManager->getSubAdmin();
315
-
316
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
317
-			$userid = $this->createNewUserId();
318
-		}
319
-
320
-		if ($this->userManager->userExists($userid)) {
321
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
322
-			throw new OCSException('User already exists', 102);
323
-		}
324
-
325
-		if ($groups !== []) {
326
-			foreach ($groups as $group) {
327
-				if (!$this->groupManager->groupExists($group)) {
328
-					throw new OCSException('group '.$group.' does not exist', 104);
329
-				}
330
-				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
331
-					throw new OCSException('insufficient privileges for group '. $group, 105);
332
-				}
333
-			}
334
-		} else {
335
-			if (!$isAdmin) {
336
-				throw new OCSException('no group specified (required for subadmins)', 106);
337
-			}
338
-		}
339
-
340
-		$subadminGroups = [];
341
-		if ($subadmin !== []) {
342
-			foreach ($subadmin as $groupid) {
343
-				$group = $this->groupManager->get($groupid);
344
-				// Check if group exists
345
-				if ($group === null) {
346
-					throw new OCSException('Subadmin group does not exist',  102);
347
-				}
348
-				// Check if trying to make subadmin of admin group
349
-				if ($group->getGID() === 'admin') {
350
-					throw new OCSException('Cannot create subadmins for admin group', 103);
351
-				}
352
-				// Check if has permission to promote subadmins
353
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
354
-					throw new OCSForbiddenException('No permissions to promote subadmins');
355
-				}
356
-				$subadminGroups[] = $group;
357
-			}
358
-		}
359
-
360
-		$generatePasswordResetToken = false;
361
-		if ($password === '') {
362
-			if ($email === '') {
363
-				throw new OCSException('To send a password link to the user an email address is required.', 108);
364
-			}
365
-
366
-			$passwordEvent = new GenerateSecurePasswordEvent();
367
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
368
-
369
-			$password = $passwordEvent->getPassword();
370
-			if ($password === null) {
371
-				// Fallback: ensure to pass password_policy in any case
372
-				$password = $this->secureRandom->generate(10)
373
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
374
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
375
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
376
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
377
-			}
378
-			$generatePasswordResetToken = true;
379
-		}
380
-
381
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
382
-			throw new OCSException('Required email address was not provided', 110);
383
-		}
384
-
385
-		try {
386
-			$newUser = $this->userManager->createUser($userid, $password);
387
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
388
-
389
-			foreach ($groups as $group) {
390
-				$this->groupManager->get($group)->addUser($newUser);
391
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
392
-			}
393
-			foreach ($subadminGroups as $group) {
394
-				$subAdminManager->createSubAdmin($newUser, $group);
395
-			}
396
-
397
-			if ($displayName !== '') {
398
-				$this->editUser($userid, 'display', $displayName);
399
-			}
400
-
401
-			if ($quota !== '') {
402
-				$this->editUser($userid, 'quota', $quota);
403
-			}
404
-
405
-			if ($language !== '') {
406
-				$this->editUser($userid, 'language', $language);
407
-			}
408
-
409
-			// Send new user mail only if a mail is set
410
-			if ($email !== '') {
411
-				$newUser->setEMailAddress($email);
412
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
413
-					try {
414
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
415
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
416
-					} catch (\Exception $e) {
417
-						// Mail could be failing hard or just be plain not configured
418
-						// Logging error as it is the hardest of the two
419
-						$this->logger->logException($e, [
420
-							'message' => "Unable to send the invitation mail to $email",
421
-							'level' => ILogger::ERROR,
422
-							'app' => 'ocs_api',
423
-						]);
424
-					}
425
-				}
426
-			}
427
-
428
-			return new DataResponse(['id' => $userid]);
429
-		} catch (HintException $e) {
430
-			$this->logger->logException($e, [
431
-				'message' => 'Failed addUser attempt with hint exception.',
432
-				'level' => ILogger::WARN,
433
-				'app' => 'ocs_api',
434
-			]);
435
-			throw new OCSException($e->getHint(), 107);
436
-		} catch (OCSException $e) {
437
-			$this->logger->logException($e, [
438
-				'message' => 'Failed addUser attempt with ocs exeption.',
439
-				'level' => ILogger::ERROR,
440
-				'app' => 'ocs_api',
441
-			]);
442
-			throw $e;
443
-		} catch (\Exception $e) {
444
-			$this->logger->logException($e, [
445
-				'message' => 'Failed addUser attempt with exception.',
446
-				'level' => ILogger::ERROR,
447
-				'app' => 'ocs_api',
448
-			]);
449
-			throw new OCSException('Bad request', 101);
450
-		}
451
-	}
452
-
453
-	/**
454
-	 * @NoAdminRequired
455
-	 * @NoSubAdminRequired
456
-	 *
457
-	 * gets user info
458
-	 *
459
-	 * @param string $userId
460
-	 * @return DataResponse
461
-	 * @throws OCSException
462
-	 */
463
-	public function getUser(string $userId): DataResponse {
464
-		$data = $this->getUserData($userId);
465
-		// getUserData returns empty array if not enough permissions
466
-		if (empty($data)) {
467
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
468
-		}
469
-		return new DataResponse($data);
470
-	}
471
-
472
-	/**
473
-	 * @NoAdminRequired
474
-	 * @NoSubAdminRequired
475
-	 *
476
-	 * gets user info from the currently logged in user
477
-	 *
478
-	 * @return DataResponse
479
-	 * @throws OCSException
480
-	 */
481
-	public function getCurrentUser(): DataResponse {
482
-		$user = $this->userSession->getUser();
483
-		if ($user) {
484
-			$data = $this->getUserData($user->getUID());
485
-			// rename "displayname" to "display-name" only for this call to keep
486
-			// the API stable.
487
-			$data['display-name'] = $data['displayname'];
488
-			unset($data['displayname']);
489
-			return new DataResponse($data);
490
-		}
491
-
492
-		throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
493
-	}
494
-
495
-	/**
496
-	 * @NoAdminRequired
497
-	 * @NoSubAdminRequired
498
-	 */
499
-	public function getEditableFields(): DataResponse {
500
-		$permittedFields = [];
501
-
502
-		// Editing self (display, email)
503
-		if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
504
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
505
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
506
-		}
507
-
508
-		if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
509
-			$shareProvider = $this->federatedShareProviderFactory->get();
510
-			if ($shareProvider->isLookupServerUploadEnabled()) {
511
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
512
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
513
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
514
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
515
-			}
516
-		}
517
-
518
-		return new DataResponse($permittedFields);
519
-	}
520
-
521
-	/**
522
-	 * @NoAdminRequired
523
-	 * @NoSubAdminRequired
524
-	 * @PasswordConfirmationRequired
525
-	 *
526
-	 * edit users
527
-	 *
528
-	 * @param string $userId
529
-	 * @param string $key
530
-	 * @param string $value
531
-	 * @return DataResponse
532
-	 * @throws OCSException
533
-	 */
534
-	public function editUser(string $userId, string $key, string $value): DataResponse {
535
-		$currentLoggedInUser = $this->userSession->getUser();
536
-
537
-		$targetUser = $this->userManager->get($userId);
538
-		if ($targetUser === null) {
539
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
540
-		}
541
-
542
-		$permittedFields = [];
543
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
544
-			// Editing self (display, email)
545
-			if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
546
-				$permittedFields[] = 'display';
547
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
548
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
549
-			}
550
-
551
-			$permittedFields[] = 'password';
552
-			if ($this->config->getSystemValue('force_language', false) === false ||
553
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
554
-				$permittedFields[] = 'language';
555
-			}
556
-
557
-			if ($this->config->getSystemValue('force_locale', false) === false ||
558
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
559
-				$permittedFields[] = 'locale';
560
-			}
561
-
562
-			if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
563
-				$shareProvider = $this->federatedShareProviderFactory->get();
564
-				if ($shareProvider->isLookupServerUploadEnabled()) {
565
-					$permittedFields[] = IAccountManager::PROPERTY_PHONE;
566
-					$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
567
-					$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
568
-					$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
569
-				}
570
-			}
571
-
572
-			// If admin they can edit their own quota
573
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
574
-				$permittedFields[] = 'quota';
575
-			}
576
-		} else {
577
-			// Check if admin / subadmin
578
-			$subAdminManager = $this->groupManager->getSubAdmin();
579
-			if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
580
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
581
-				// They have permissions over the user
582
-				$permittedFields[] = 'display';
583
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
584
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
585
-				$permittedFields[] = 'password';
586
-				$permittedFields[] = 'language';
587
-				$permittedFields[] = 'locale';
588
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
589
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
590
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
591
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
592
-				$permittedFields[] = 'quota';
593
-			} else {
594
-				// No rights
595
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
596
-			}
597
-		}
598
-		// Check if permitted to edit this field
599
-		if (!in_array($key, $permittedFields)) {
600
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
601
-		}
602
-		// Process the edit
603
-		switch ($key) {
604
-			case 'display':
605
-			case IAccountManager::PROPERTY_DISPLAYNAME:
606
-				$targetUser->setDisplayName($value);
607
-				break;
608
-			case 'quota':
609
-				$quota = $value;
610
-				if ($quota !== 'none' && $quota !== 'default') {
611
-					if (is_numeric($quota)) {
612
-						$quota = (float) $quota;
613
-					} else {
614
-						$quota = \OCP\Util::computerFileSize($quota);
615
-					}
616
-					if ($quota === false) {
617
-						throw new OCSException('Invalid quota value '.$value, 103);
618
-					}
619
-					if ($quota === -1) {
620
-						$quota = 'none';
621
-					} else {
622
-						$quota = \OCP\Util::humanFileSize($quota);
623
-					}
624
-				}
625
-				$targetUser->setQuota($quota);
626
-				break;
627
-			case 'password':
628
-				try {
629
-					if (!$targetUser->canChangePassword()) {
630
-						throw new OCSException('Setting the password is not supported by the users backend', 103);
631
-					}
632
-					$targetUser->setPassword($value);
633
-				} catch (HintException $e) { // password policy error
634
-					throw new OCSException($e->getMessage(), 103);
635
-				}
636
-				break;
637
-			case 'language':
638
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
639
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
640
-					throw new OCSException('Invalid language', 102);
641
-				}
642
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
643
-				break;
644
-			case 'locale':
645
-				if (!$this->l10nFactory->localeExists($value)) {
646
-					throw new OCSException('Invalid locale', 102);
647
-				}
648
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
649
-				break;
650
-			case IAccountManager::PROPERTY_EMAIL:
651
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
652
-					$targetUser->setEMailAddress($value);
653
-				} else {
654
-					throw new OCSException('', 102);
655
-				}
656
-				break;
657
-			case IAccountManager::PROPERTY_PHONE:
658
-			case IAccountManager::PROPERTY_ADDRESS:
659
-			case IAccountManager::PROPERTY_WEBSITE:
660
-			case IAccountManager::PROPERTY_TWITTER:
661
-				$userAccount = $this->accountManager->getUser($targetUser);
662
-				if ($userAccount[$key]['value'] !== $value) {
663
-					$userAccount[$key]['value'] = $value;
664
-					try {
665
-						$this->accountManager->updateUser($targetUser, $userAccount, true);
666
-					} catch (\InvalidArgumentException $e) {
667
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
668
-					}
669
-				}
670
-				break;
671
-			default:
672
-				throw new OCSException('', 103);
673
-		}
674
-		return new DataResponse();
675
-	}
676
-
677
-	/**
678
-	 * @PasswordConfirmationRequired
679
-	 * @NoAdminRequired
680
-	 *
681
-	 * @param string $userId
682
-	 *
683
-	 * @return DataResponse
684
-	 *
685
-	 * @throws OCSException
686
-	 */
687
-	public function wipeUserDevices(string $userId): DataResponse {
688
-		/** @var IUser $currentLoggedInUser */
689
-		$currentLoggedInUser = $this->userSession->getUser();
690
-
691
-		$targetUser = $this->userManager->get($userId);
692
-
693
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
694
-			throw new OCSException('', 101);
695
-		}
696
-
697
-		// If not permitted
698
-		$subAdminManager = $this->groupManager->getSubAdmin();
699
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
700
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
701
-		}
702
-
703
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
704
-
705
-		return new DataResponse();
706
-	}
707
-
708
-	/**
709
-	 * @PasswordConfirmationRequired
710
-	 * @NoAdminRequired
711
-	 *
712
-	 * @param string $userId
713
-	 * @return DataResponse
714
-	 * @throws OCSException
715
-	 */
716
-	public function deleteUser(string $userId): DataResponse {
717
-		$currentLoggedInUser = $this->userSession->getUser();
718
-
719
-		$targetUser = $this->userManager->get($userId);
720
-
721
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
722
-			throw new OCSException('', 101);
723
-		}
724
-
725
-		// If not permitted
726
-		$subAdminManager = $this->groupManager->getSubAdmin();
727
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
728
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
729
-		}
730
-
731
-		// Go ahead with the delete
732
-		if ($targetUser->delete()) {
733
-			return new DataResponse();
734
-		} else {
735
-			throw new OCSException('', 101);
736
-		}
737
-	}
738
-
739
-	/**
740
-	 * @PasswordConfirmationRequired
741
-	 * @NoAdminRequired
742
-	 *
743
-	 * @param string $userId
744
-	 * @return DataResponse
745
-	 * @throws OCSException
746
-	 * @throws OCSForbiddenException
747
-	 */
748
-	public function disableUser(string $userId): DataResponse {
749
-		return $this->setEnabled($userId, false);
750
-	}
751
-
752
-	/**
753
-	 * @PasswordConfirmationRequired
754
-	 * @NoAdminRequired
755
-	 *
756
-	 * @param string $userId
757
-	 * @return DataResponse
758
-	 * @throws OCSException
759
-	 * @throws OCSForbiddenException
760
-	 */
761
-	public function enableUser(string $userId): DataResponse {
762
-		return $this->setEnabled($userId, true);
763
-	}
764
-
765
-	/**
766
-	 * @param string $userId
767
-	 * @param bool $value
768
-	 * @return DataResponse
769
-	 * @throws OCSException
770
-	 */
771
-	private function setEnabled(string $userId, bool $value): DataResponse {
772
-		$currentLoggedInUser = $this->userSession->getUser();
773
-
774
-		$targetUser = $this->userManager->get($userId);
775
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
776
-			throw new OCSException('', 101);
777
-		}
778
-
779
-		// If not permitted
780
-		$subAdminManager = $this->groupManager->getSubAdmin();
781
-		if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
782
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
783
-		}
784
-
785
-		// enable/disable the user now
786
-		$targetUser->setEnabled($value);
787
-		return new DataResponse();
788
-	}
789
-
790
-	/**
791
-	 * @NoAdminRequired
792
-	 * @NoSubAdminRequired
793
-	 *
794
-	 * @param string $userId
795
-	 * @return DataResponse
796
-	 * @throws OCSException
797
-	 */
798
-	public function getUsersGroups(string $userId): DataResponse {
799
-		$loggedInUser = $this->userSession->getUser();
800
-
801
-		$targetUser = $this->userManager->get($userId);
802
-		if ($targetUser === null) {
803
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
804
-		}
805
-
806
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
807
-			// Self lookup or admin lookup
808
-			return new DataResponse([
809
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
810
-			]);
811
-		} else {
812
-			$subAdminManager = $this->groupManager->getSubAdmin();
813
-
814
-			// Looking up someone else
815
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
816
-				// Return the group that the method caller is subadmin of for the user in question
817
-				/** @var IGroup[] $getSubAdminsGroups */
818
-				$getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
819
-				foreach ($getSubAdminsGroups as $key => $group) {
820
-					$getSubAdminsGroups[$key] = $group->getGID();
821
-				}
822
-				$groups = array_intersect(
823
-					$getSubAdminsGroups,
824
-					$this->groupManager->getUserGroupIds($targetUser)
825
-				);
826
-				return new DataResponse(['groups' => $groups]);
827
-			} else {
828
-				// Not permitted
829
-				throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
830
-			}
831
-		}
832
-	}
833
-
834
-	/**
835
-	 * @PasswordConfirmationRequired
836
-	 * @NoAdminRequired
837
-	 *
838
-	 * @param string $userId
839
-	 * @param string $groupid
840
-	 * @return DataResponse
841
-	 * @throws OCSException
842
-	 */
843
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
844
-		if ($groupid === '') {
845
-			throw new OCSException('', 101);
846
-		}
847
-
848
-		$group = $this->groupManager->get($groupid);
849
-		$targetUser = $this->userManager->get($userId);
850
-		if ($group === null) {
851
-			throw new OCSException('', 102);
852
-		}
853
-		if ($targetUser === null) {
854
-			throw new OCSException('', 103);
855
-		}
856
-
857
-		// If they're not an admin, check they are a subadmin of the group in question
858
-		$loggedInUser = $this->userSession->getUser();
859
-		$subAdminManager = $this->groupManager->getSubAdmin();
860
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
861
-			throw new OCSException('', 104);
862
-		}
863
-
864
-		// Add user to group
865
-		$group->addUser($targetUser);
866
-		return new DataResponse();
867
-	}
868
-
869
-	/**
870
-	 * @PasswordConfirmationRequired
871
-	 * @NoAdminRequired
872
-	 *
873
-	 * @param string $userId
874
-	 * @param string $groupid
875
-	 * @return DataResponse
876
-	 * @throws OCSException
877
-	 */
878
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
879
-		$loggedInUser = $this->userSession->getUser();
880
-
881
-		if ($groupid === null || trim($groupid) === '') {
882
-			throw new OCSException('', 101);
883
-		}
884
-
885
-		$group = $this->groupManager->get($groupid);
886
-		if ($group === null) {
887
-			throw new OCSException('', 102);
888
-		}
889
-
890
-		$targetUser = $this->userManager->get($userId);
891
-		if ($targetUser === null) {
892
-			throw new OCSException('', 103);
893
-		}
894
-
895
-		// If they're not an admin, check they are a subadmin of the group in question
896
-		$subAdminManager = $this->groupManager->getSubAdmin();
897
-		if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
898
-			throw new OCSException('', 104);
899
-		}
900
-
901
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
902
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
903
-			if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
904
-				if ($group->getGID() === 'admin') {
905
-					throw new OCSException('Cannot remove yourself from the admin group', 105);
906
-				}
907
-			} else {
908
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
909
-				throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
910
-			}
911
-		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
912
-			/** @var IGroup[] $subAdminGroups */
913
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
914
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
915
-				return $subAdminGroup->getGID();
916
-			}, $subAdminGroups);
917
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
918
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
919
-
920
-			if (count($userSubAdminGroups) <= 1) {
921
-				// Subadmin must not be able to remove a user from all their subadmin groups.
922
-				throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
923
-			}
924
-		}
925
-
926
-		// Remove user from group
927
-		$group->removeUser($targetUser);
928
-		return new DataResponse();
929
-	}
930
-
931
-	/**
932
-	 * Creates a subadmin
933
-	 *
934
-	 * @PasswordConfirmationRequired
935
-	 *
936
-	 * @param string $userId
937
-	 * @param string $groupid
938
-	 * @return DataResponse
939
-	 * @throws OCSException
940
-	 */
941
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
942
-		$group = $this->groupManager->get($groupid);
943
-		$user = $this->userManager->get($userId);
944
-
945
-		// Check if the user exists
946
-		if ($user === null) {
947
-			throw new OCSException('User does not exist', 101);
948
-		}
949
-		// Check if group exists
950
-		if ($group === null) {
951
-			throw new OCSException('Group does not exist',  102);
952
-		}
953
-		// Check if trying to make subadmin of admin group
954
-		if ($group->getGID() === 'admin') {
955
-			throw new OCSException('Cannot create subadmins for admin group', 103);
956
-		}
957
-
958
-		$subAdminManager = $this->groupManager->getSubAdmin();
959
-
960
-		// We cannot be subadmin twice
961
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
962
-			return new DataResponse();
963
-		}
964
-		// Go
965
-		$subAdminManager->createSubAdmin($user, $group);
966
-		return new DataResponse();
967
-	}
968
-
969
-	/**
970
-	 * Removes a subadmin from a group
971
-	 *
972
-	 * @PasswordConfirmationRequired
973
-	 *
974
-	 * @param string $userId
975
-	 * @param string $groupid
976
-	 * @return DataResponse
977
-	 * @throws OCSException
978
-	 */
979
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
980
-		$group = $this->groupManager->get($groupid);
981
-		$user = $this->userManager->get($userId);
982
-		$subAdminManager = $this->groupManager->getSubAdmin();
983
-
984
-		// Check if the user exists
985
-		if ($user === null) {
986
-			throw new OCSException('User does not exist', 101);
987
-		}
988
-		// Check if the group exists
989
-		if ($group === null) {
990
-			throw new OCSException('Group does not exist', 101);
991
-		}
992
-		// Check if they are a subadmin of this said group
993
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
994
-			throw new OCSException('User is not a subadmin of this group', 102);
995
-		}
996
-
997
-		// Go
998
-		$subAdminManager->deleteSubAdmin($user, $group);
999
-		return new DataResponse();
1000
-	}
1001
-
1002
-	/**
1003
-	 * Get the groups a user is a subadmin of
1004
-	 *
1005
-	 * @param string $userId
1006
-	 * @return DataResponse
1007
-	 * @throws OCSException
1008
-	 */
1009
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1010
-		$groups = $this->getUserSubAdminGroupsData($userId);
1011
-		return new DataResponse($groups);
1012
-	}
1013
-
1014
-	/**
1015
-	 * @NoAdminRequired
1016
-	 * @PasswordConfirmationRequired
1017
-	 *
1018
-	 * resend welcome message
1019
-	 *
1020
-	 * @param string $userId
1021
-	 * @return DataResponse
1022
-	 * @throws OCSException
1023
-	 */
1024
-	public function resendWelcomeMessage(string $userId): DataResponse {
1025
-		$currentLoggedInUser = $this->userSession->getUser();
1026
-
1027
-		$targetUser = $this->userManager->get($userId);
1028
-		if ($targetUser === null) {
1029
-			throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
1030
-		}
1031
-
1032
-		// Check if admin / subadmin
1033
-		$subAdminManager = $this->groupManager->getSubAdmin();
1034
-		if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1035
-			&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1036
-			// No rights
1037
-			throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
1038
-		}
1039
-
1040
-		$email = $targetUser->getEMailAddress();
1041
-		if ($email === '' || $email === null) {
1042
-			throw new OCSException('Email address not available', 101);
1043
-		}
1044
-
1045
-		try {
1046
-			$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1047
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1048
-		} catch (\Exception $e) {
1049
-			$this->logger->logException($e, [
1050
-				'message' => "Can't send new user mail to $email",
1051
-				'level' => ILogger::ERROR,
1052
-				'app' => 'settings',
1053
-			]);
1054
-			throw new OCSException('Sending email failed', 102);
1055
-		}
1056
-
1057
-		return new DataResponse();
1058
-	}
75
+    /** @var IAppManager */
76
+    private $appManager;
77
+    /** @var IURLGenerator */
78
+    protected $urlGenerator;
79
+    /** @var ILogger */
80
+    private $logger;
81
+    /** @var IFactory */
82
+    protected $l10nFactory;
83
+    /** @var NewUserMailHelper */
84
+    private $newUserMailHelper;
85
+    /** @var FederatedShareProviderFactory */
86
+    private $federatedShareProviderFactory;
87
+    /** @var ISecureRandom */
88
+    private $secureRandom;
89
+    /** @var RemoteWipe */
90
+    private $remoteWipe;
91
+    /** @var IEventDispatcher */
92
+    private $eventDispatcher;
93
+
94
+    public function __construct(string $appName,
95
+                                IRequest $request,
96
+                                IUserManager $userManager,
97
+                                IConfig $config,
98
+                                IAppManager $appManager,
99
+                                IGroupManager $groupManager,
100
+                                IUserSession $userSession,
101
+                                AccountManager $accountManager,
102
+                                IURLGenerator $urlGenerator,
103
+                                ILogger $logger,
104
+                                IFactory $l10nFactory,
105
+                                NewUserMailHelper $newUserMailHelper,
106
+                                FederatedShareProviderFactory $federatedShareProviderFactory,
107
+                                ISecureRandom $secureRandom,
108
+                                RemoteWipe $remoteWipe,
109
+                                IEventDispatcher $eventDispatcher) {
110
+        parent::__construct($appName,
111
+                            $request,
112
+                            $userManager,
113
+                            $config,
114
+                            $groupManager,
115
+                            $userSession,
116
+                            $accountManager,
117
+                            $l10nFactory);
118
+
119
+        $this->appManager = $appManager;
120
+        $this->urlGenerator = $urlGenerator;
121
+        $this->logger = $logger;
122
+        $this->l10nFactory = $l10nFactory;
123
+        $this->newUserMailHelper = $newUserMailHelper;
124
+        $this->federatedShareProviderFactory = $federatedShareProviderFactory;
125
+        $this->secureRandom = $secureRandom;
126
+        $this->remoteWipe = $remoteWipe;
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
+        $normalizedNumberToKey = [];
233
+        foreach ($search as $key => $phoneNumbers) {
234
+            foreach ($phoneNumbers as $phone) {
235
+                try {
236
+                    $phoneNumber = $phoneUtil->parse($phone, $location);
237
+                    if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
238
+                        $normalizedNumber = $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
239
+                        $normalizedNumberToKey[$normalizedNumber] = (string) $key;
240
+                    }
241
+                } catch (NumberParseException $e) {
242
+                }
243
+            }
244
+        }
245
+
246
+        $phoneNumbers = array_keys($normalizedNumberToKey);
247
+
248
+        if (empty($phoneNumbers)) {
249
+            return new DataResponse();
250
+        }
251
+
252
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
253
+
254
+        if (empty($userMatches)) {
255
+            return new DataResponse();
256
+        }
257
+
258
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
259
+        if (strpos($cloudUrl, 'http://') === 0) {
260
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
261
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
262
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
263
+        }
264
+
265
+        $matches = [];
266
+        foreach ($userMatches as $phone => $userId) {
267
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
268
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
269
+        }
270
+
271
+        return new DataResponse($matches);
272
+    }
273
+
274
+    /**
275
+     * @throws OCSException
276
+     */
277
+    private function createNewUserId(): string {
278
+        $attempts = 0;
279
+        do {
280
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
281
+            if (!$this->userManager->userExists($uidCandidate)) {
282
+                return $uidCandidate;
283
+            }
284
+            $attempts++;
285
+        } while ($attempts < 10);
286
+        throw new OCSException('Could not create non-existing user id', 111);
287
+    }
288
+
289
+    /**
290
+     * @PasswordConfirmationRequired
291
+     * @NoAdminRequired
292
+     *
293
+     * @param string $userid
294
+     * @param string $password
295
+     * @param string $displayName
296
+     * @param string $email
297
+     * @param array $groups
298
+     * @param array $subadmin
299
+     * @param string $quota
300
+     * @param string $language
301
+     * @return DataResponse
302
+     * @throws OCSException
303
+     */
304
+    public function addUser(string $userid,
305
+                            string $password = '',
306
+                            string $displayName = '',
307
+                            string $email = '',
308
+                            array $groups = [],
309
+                            array $subadmin = [],
310
+                            string $quota = '',
311
+                            string $language = ''): DataResponse {
312
+        $user = $this->userSession->getUser();
313
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
314
+        $subAdminManager = $this->groupManager->getSubAdmin();
315
+
316
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
317
+            $userid = $this->createNewUserId();
318
+        }
319
+
320
+        if ($this->userManager->userExists($userid)) {
321
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
322
+            throw new OCSException('User already exists', 102);
323
+        }
324
+
325
+        if ($groups !== []) {
326
+            foreach ($groups as $group) {
327
+                if (!$this->groupManager->groupExists($group)) {
328
+                    throw new OCSException('group '.$group.' does not exist', 104);
329
+                }
330
+                if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
331
+                    throw new OCSException('insufficient privileges for group '. $group, 105);
332
+                }
333
+            }
334
+        } else {
335
+            if (!$isAdmin) {
336
+                throw new OCSException('no group specified (required for subadmins)', 106);
337
+            }
338
+        }
339
+
340
+        $subadminGroups = [];
341
+        if ($subadmin !== []) {
342
+            foreach ($subadmin as $groupid) {
343
+                $group = $this->groupManager->get($groupid);
344
+                // Check if group exists
345
+                if ($group === null) {
346
+                    throw new OCSException('Subadmin group does not exist',  102);
347
+                }
348
+                // Check if trying to make subadmin of admin group
349
+                if ($group->getGID() === 'admin') {
350
+                    throw new OCSException('Cannot create subadmins for admin group', 103);
351
+                }
352
+                // Check if has permission to promote subadmins
353
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
354
+                    throw new OCSForbiddenException('No permissions to promote subadmins');
355
+                }
356
+                $subadminGroups[] = $group;
357
+            }
358
+        }
359
+
360
+        $generatePasswordResetToken = false;
361
+        if ($password === '') {
362
+            if ($email === '') {
363
+                throw new OCSException('To send a password link to the user an email address is required.', 108);
364
+            }
365
+
366
+            $passwordEvent = new GenerateSecurePasswordEvent();
367
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
368
+
369
+            $password = $passwordEvent->getPassword();
370
+            if ($password === null) {
371
+                // Fallback: ensure to pass password_policy in any case
372
+                $password = $this->secureRandom->generate(10)
373
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
374
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
375
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
376
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
377
+            }
378
+            $generatePasswordResetToken = true;
379
+        }
380
+
381
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
382
+            throw new OCSException('Required email address was not provided', 110);
383
+        }
384
+
385
+        try {
386
+            $newUser = $this->userManager->createUser($userid, $password);
387
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
388
+
389
+            foreach ($groups as $group) {
390
+                $this->groupManager->get($group)->addUser($newUser);
391
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
392
+            }
393
+            foreach ($subadminGroups as $group) {
394
+                $subAdminManager->createSubAdmin($newUser, $group);
395
+            }
396
+
397
+            if ($displayName !== '') {
398
+                $this->editUser($userid, 'display', $displayName);
399
+            }
400
+
401
+            if ($quota !== '') {
402
+                $this->editUser($userid, 'quota', $quota);
403
+            }
404
+
405
+            if ($language !== '') {
406
+                $this->editUser($userid, 'language', $language);
407
+            }
408
+
409
+            // Send new user mail only if a mail is set
410
+            if ($email !== '') {
411
+                $newUser->setEMailAddress($email);
412
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
413
+                    try {
414
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
415
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
416
+                    } catch (\Exception $e) {
417
+                        // Mail could be failing hard or just be plain not configured
418
+                        // Logging error as it is the hardest of the two
419
+                        $this->logger->logException($e, [
420
+                            'message' => "Unable to send the invitation mail to $email",
421
+                            'level' => ILogger::ERROR,
422
+                            'app' => 'ocs_api',
423
+                        ]);
424
+                    }
425
+                }
426
+            }
427
+
428
+            return new DataResponse(['id' => $userid]);
429
+        } catch (HintException $e) {
430
+            $this->logger->logException($e, [
431
+                'message' => 'Failed addUser attempt with hint exception.',
432
+                'level' => ILogger::WARN,
433
+                'app' => 'ocs_api',
434
+            ]);
435
+            throw new OCSException($e->getHint(), 107);
436
+        } catch (OCSException $e) {
437
+            $this->logger->logException($e, [
438
+                'message' => 'Failed addUser attempt with ocs exeption.',
439
+                'level' => ILogger::ERROR,
440
+                'app' => 'ocs_api',
441
+            ]);
442
+            throw $e;
443
+        } catch (\Exception $e) {
444
+            $this->logger->logException($e, [
445
+                'message' => 'Failed addUser attempt with exception.',
446
+                'level' => ILogger::ERROR,
447
+                'app' => 'ocs_api',
448
+            ]);
449
+            throw new OCSException('Bad request', 101);
450
+        }
451
+    }
452
+
453
+    /**
454
+     * @NoAdminRequired
455
+     * @NoSubAdminRequired
456
+     *
457
+     * gets user info
458
+     *
459
+     * @param string $userId
460
+     * @return DataResponse
461
+     * @throws OCSException
462
+     */
463
+    public function getUser(string $userId): DataResponse {
464
+        $data = $this->getUserData($userId);
465
+        // getUserData returns empty array if not enough permissions
466
+        if (empty($data)) {
467
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
468
+        }
469
+        return new DataResponse($data);
470
+    }
471
+
472
+    /**
473
+     * @NoAdminRequired
474
+     * @NoSubAdminRequired
475
+     *
476
+     * gets user info from the currently logged in user
477
+     *
478
+     * @return DataResponse
479
+     * @throws OCSException
480
+     */
481
+    public function getCurrentUser(): DataResponse {
482
+        $user = $this->userSession->getUser();
483
+        if ($user) {
484
+            $data = $this->getUserData($user->getUID());
485
+            // rename "displayname" to "display-name" only for this call to keep
486
+            // the API stable.
487
+            $data['display-name'] = $data['displayname'];
488
+            unset($data['displayname']);
489
+            return new DataResponse($data);
490
+        }
491
+
492
+        throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
493
+    }
494
+
495
+    /**
496
+     * @NoAdminRequired
497
+     * @NoSubAdminRequired
498
+     */
499
+    public function getEditableFields(): DataResponse {
500
+        $permittedFields = [];
501
+
502
+        // Editing self (display, email)
503
+        if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
504
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
505
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
506
+        }
507
+
508
+        if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
509
+            $shareProvider = $this->federatedShareProviderFactory->get();
510
+            if ($shareProvider->isLookupServerUploadEnabled()) {
511
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
512
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
513
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
514
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
515
+            }
516
+        }
517
+
518
+        return new DataResponse($permittedFields);
519
+    }
520
+
521
+    /**
522
+     * @NoAdminRequired
523
+     * @NoSubAdminRequired
524
+     * @PasswordConfirmationRequired
525
+     *
526
+     * edit users
527
+     *
528
+     * @param string $userId
529
+     * @param string $key
530
+     * @param string $value
531
+     * @return DataResponse
532
+     * @throws OCSException
533
+     */
534
+    public function editUser(string $userId, string $key, string $value): DataResponse {
535
+        $currentLoggedInUser = $this->userSession->getUser();
536
+
537
+        $targetUser = $this->userManager->get($userId);
538
+        if ($targetUser === null) {
539
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
540
+        }
541
+
542
+        $permittedFields = [];
543
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
544
+            // Editing self (display, email)
545
+            if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) {
546
+                $permittedFields[] = 'display';
547
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
548
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
549
+            }
550
+
551
+            $permittedFields[] = 'password';
552
+            if ($this->config->getSystemValue('force_language', false) === false ||
553
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
554
+                $permittedFields[] = 'language';
555
+            }
556
+
557
+            if ($this->config->getSystemValue('force_locale', false) === false ||
558
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
559
+                $permittedFields[] = 'locale';
560
+            }
561
+
562
+            if ($this->appManager->isEnabledForUser('federatedfilesharing')) {
563
+                $shareProvider = $this->federatedShareProviderFactory->get();
564
+                if ($shareProvider->isLookupServerUploadEnabled()) {
565
+                    $permittedFields[] = IAccountManager::PROPERTY_PHONE;
566
+                    $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
567
+                    $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
568
+                    $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
569
+                }
570
+            }
571
+
572
+            // If admin they can edit their own quota
573
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
574
+                $permittedFields[] = 'quota';
575
+            }
576
+        } else {
577
+            // Check if admin / subadmin
578
+            $subAdminManager = $this->groupManager->getSubAdmin();
579
+            if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())
580
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
581
+                // They have permissions over the user
582
+                $permittedFields[] = 'display';
583
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
584
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
585
+                $permittedFields[] = 'password';
586
+                $permittedFields[] = 'language';
587
+                $permittedFields[] = 'locale';
588
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
589
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
590
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
591
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
592
+                $permittedFields[] = 'quota';
593
+            } else {
594
+                // No rights
595
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
596
+            }
597
+        }
598
+        // Check if permitted to edit this field
599
+        if (!in_array($key, $permittedFields)) {
600
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
601
+        }
602
+        // Process the edit
603
+        switch ($key) {
604
+            case 'display':
605
+            case IAccountManager::PROPERTY_DISPLAYNAME:
606
+                $targetUser->setDisplayName($value);
607
+                break;
608
+            case 'quota':
609
+                $quota = $value;
610
+                if ($quota !== 'none' && $quota !== 'default') {
611
+                    if (is_numeric($quota)) {
612
+                        $quota = (float) $quota;
613
+                    } else {
614
+                        $quota = \OCP\Util::computerFileSize($quota);
615
+                    }
616
+                    if ($quota === false) {
617
+                        throw new OCSException('Invalid quota value '.$value, 103);
618
+                    }
619
+                    if ($quota === -1) {
620
+                        $quota = 'none';
621
+                    } else {
622
+                        $quota = \OCP\Util::humanFileSize($quota);
623
+                    }
624
+                }
625
+                $targetUser->setQuota($quota);
626
+                break;
627
+            case 'password':
628
+                try {
629
+                    if (!$targetUser->canChangePassword()) {
630
+                        throw new OCSException('Setting the password is not supported by the users backend', 103);
631
+                    }
632
+                    $targetUser->setPassword($value);
633
+                } catch (HintException $e) { // password policy error
634
+                    throw new OCSException($e->getMessage(), 103);
635
+                }
636
+                break;
637
+            case 'language':
638
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
639
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
640
+                    throw new OCSException('Invalid language', 102);
641
+                }
642
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
643
+                break;
644
+            case 'locale':
645
+                if (!$this->l10nFactory->localeExists($value)) {
646
+                    throw new OCSException('Invalid locale', 102);
647
+                }
648
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
649
+                break;
650
+            case IAccountManager::PROPERTY_EMAIL:
651
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
652
+                    $targetUser->setEMailAddress($value);
653
+                } else {
654
+                    throw new OCSException('', 102);
655
+                }
656
+                break;
657
+            case IAccountManager::PROPERTY_PHONE:
658
+            case IAccountManager::PROPERTY_ADDRESS:
659
+            case IAccountManager::PROPERTY_WEBSITE:
660
+            case IAccountManager::PROPERTY_TWITTER:
661
+                $userAccount = $this->accountManager->getUser($targetUser);
662
+                if ($userAccount[$key]['value'] !== $value) {
663
+                    $userAccount[$key]['value'] = $value;
664
+                    try {
665
+                        $this->accountManager->updateUser($targetUser, $userAccount, true);
666
+                    } catch (\InvalidArgumentException $e) {
667
+                        throw new OCSException('Invalid ' . $e->getMessage(), 102);
668
+                    }
669
+                }
670
+                break;
671
+            default:
672
+                throw new OCSException('', 103);
673
+        }
674
+        return new DataResponse();
675
+    }
676
+
677
+    /**
678
+     * @PasswordConfirmationRequired
679
+     * @NoAdminRequired
680
+     *
681
+     * @param string $userId
682
+     *
683
+     * @return DataResponse
684
+     *
685
+     * @throws OCSException
686
+     */
687
+    public function wipeUserDevices(string $userId): DataResponse {
688
+        /** @var IUser $currentLoggedInUser */
689
+        $currentLoggedInUser = $this->userSession->getUser();
690
+
691
+        $targetUser = $this->userManager->get($userId);
692
+
693
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
694
+            throw new OCSException('', 101);
695
+        }
696
+
697
+        // If not permitted
698
+        $subAdminManager = $this->groupManager->getSubAdmin();
699
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
700
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
701
+        }
702
+
703
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
704
+
705
+        return new DataResponse();
706
+    }
707
+
708
+    /**
709
+     * @PasswordConfirmationRequired
710
+     * @NoAdminRequired
711
+     *
712
+     * @param string $userId
713
+     * @return DataResponse
714
+     * @throws OCSException
715
+     */
716
+    public function deleteUser(string $userId): DataResponse {
717
+        $currentLoggedInUser = $this->userSession->getUser();
718
+
719
+        $targetUser = $this->userManager->get($userId);
720
+
721
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
722
+            throw new OCSException('', 101);
723
+        }
724
+
725
+        // If not permitted
726
+        $subAdminManager = $this->groupManager->getSubAdmin();
727
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
728
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
729
+        }
730
+
731
+        // Go ahead with the delete
732
+        if ($targetUser->delete()) {
733
+            return new DataResponse();
734
+        } else {
735
+            throw new OCSException('', 101);
736
+        }
737
+    }
738
+
739
+    /**
740
+     * @PasswordConfirmationRequired
741
+     * @NoAdminRequired
742
+     *
743
+     * @param string $userId
744
+     * @return DataResponse
745
+     * @throws OCSException
746
+     * @throws OCSForbiddenException
747
+     */
748
+    public function disableUser(string $userId): DataResponse {
749
+        return $this->setEnabled($userId, false);
750
+    }
751
+
752
+    /**
753
+     * @PasswordConfirmationRequired
754
+     * @NoAdminRequired
755
+     *
756
+     * @param string $userId
757
+     * @return DataResponse
758
+     * @throws OCSException
759
+     * @throws OCSForbiddenException
760
+     */
761
+    public function enableUser(string $userId): DataResponse {
762
+        return $this->setEnabled($userId, true);
763
+    }
764
+
765
+    /**
766
+     * @param string $userId
767
+     * @param bool $value
768
+     * @return DataResponse
769
+     * @throws OCSException
770
+     */
771
+    private function setEnabled(string $userId, bool $value): DataResponse {
772
+        $currentLoggedInUser = $this->userSession->getUser();
773
+
774
+        $targetUser = $this->userManager->get($userId);
775
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
776
+            throw new OCSException('', 101);
777
+        }
778
+
779
+        // If not permitted
780
+        $subAdminManager = $this->groupManager->getSubAdmin();
781
+        if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
782
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
783
+        }
784
+
785
+        // enable/disable the user now
786
+        $targetUser->setEnabled($value);
787
+        return new DataResponse();
788
+    }
789
+
790
+    /**
791
+     * @NoAdminRequired
792
+     * @NoSubAdminRequired
793
+     *
794
+     * @param string $userId
795
+     * @return DataResponse
796
+     * @throws OCSException
797
+     */
798
+    public function getUsersGroups(string $userId): DataResponse {
799
+        $loggedInUser = $this->userSession->getUser();
800
+
801
+        $targetUser = $this->userManager->get($userId);
802
+        if ($targetUser === null) {
803
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
804
+        }
805
+
806
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
807
+            // Self lookup or admin lookup
808
+            return new DataResponse([
809
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
810
+            ]);
811
+        } else {
812
+            $subAdminManager = $this->groupManager->getSubAdmin();
813
+
814
+            // Looking up someone else
815
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
816
+                // Return the group that the method caller is subadmin of for the user in question
817
+                /** @var IGroup[] $getSubAdminsGroups */
818
+                $getSubAdminsGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
819
+                foreach ($getSubAdminsGroups as $key => $group) {
820
+                    $getSubAdminsGroups[$key] = $group->getGID();
821
+                }
822
+                $groups = array_intersect(
823
+                    $getSubAdminsGroups,
824
+                    $this->groupManager->getUserGroupIds($targetUser)
825
+                );
826
+                return new DataResponse(['groups' => $groups]);
827
+            } else {
828
+                // Not permitted
829
+                throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
830
+            }
831
+        }
832
+    }
833
+
834
+    /**
835
+     * @PasswordConfirmationRequired
836
+     * @NoAdminRequired
837
+     *
838
+     * @param string $userId
839
+     * @param string $groupid
840
+     * @return DataResponse
841
+     * @throws OCSException
842
+     */
843
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
844
+        if ($groupid === '') {
845
+            throw new OCSException('', 101);
846
+        }
847
+
848
+        $group = $this->groupManager->get($groupid);
849
+        $targetUser = $this->userManager->get($userId);
850
+        if ($group === null) {
851
+            throw new OCSException('', 102);
852
+        }
853
+        if ($targetUser === null) {
854
+            throw new OCSException('', 103);
855
+        }
856
+
857
+        // If they're not an admin, check they are a subadmin of the group in question
858
+        $loggedInUser = $this->userSession->getUser();
859
+        $subAdminManager = $this->groupManager->getSubAdmin();
860
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
861
+            throw new OCSException('', 104);
862
+        }
863
+
864
+        // Add user to group
865
+        $group->addUser($targetUser);
866
+        return new DataResponse();
867
+    }
868
+
869
+    /**
870
+     * @PasswordConfirmationRequired
871
+     * @NoAdminRequired
872
+     *
873
+     * @param string $userId
874
+     * @param string $groupid
875
+     * @return DataResponse
876
+     * @throws OCSException
877
+     */
878
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
879
+        $loggedInUser = $this->userSession->getUser();
880
+
881
+        if ($groupid === null || trim($groupid) === '') {
882
+            throw new OCSException('', 101);
883
+        }
884
+
885
+        $group = $this->groupManager->get($groupid);
886
+        if ($group === null) {
887
+            throw new OCSException('', 102);
888
+        }
889
+
890
+        $targetUser = $this->userManager->get($userId);
891
+        if ($targetUser === null) {
892
+            throw new OCSException('', 103);
893
+        }
894
+
895
+        // If they're not an admin, check they are a subadmin of the group in question
896
+        $subAdminManager = $this->groupManager->getSubAdmin();
897
+        if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
898
+            throw new OCSException('', 104);
899
+        }
900
+
901
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
902
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
903
+            if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
904
+                if ($group->getGID() === 'admin') {
905
+                    throw new OCSException('Cannot remove yourself from the admin group', 105);
906
+                }
907
+            } else {
908
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
909
+                throw new OCSException('Cannot remove yourself from this group as you are a SubAdmin', 105);
910
+            }
911
+        } elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
912
+            /** @var IGroup[] $subAdminGroups */
913
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
914
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
915
+                return $subAdminGroup->getGID();
916
+            }, $subAdminGroups);
917
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
918
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
919
+
920
+            if (count($userSubAdminGroups) <= 1) {
921
+                // Subadmin must not be able to remove a user from all their subadmin groups.
922
+                throw new OCSException('Not viable to remove user from the last group you are SubAdmin of', 105);
923
+            }
924
+        }
925
+
926
+        // Remove user from group
927
+        $group->removeUser($targetUser);
928
+        return new DataResponse();
929
+    }
930
+
931
+    /**
932
+     * Creates a subadmin
933
+     *
934
+     * @PasswordConfirmationRequired
935
+     *
936
+     * @param string $userId
937
+     * @param string $groupid
938
+     * @return DataResponse
939
+     * @throws OCSException
940
+     */
941
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
942
+        $group = $this->groupManager->get($groupid);
943
+        $user = $this->userManager->get($userId);
944
+
945
+        // Check if the user exists
946
+        if ($user === null) {
947
+            throw new OCSException('User does not exist', 101);
948
+        }
949
+        // Check if group exists
950
+        if ($group === null) {
951
+            throw new OCSException('Group does not exist',  102);
952
+        }
953
+        // Check if trying to make subadmin of admin group
954
+        if ($group->getGID() === 'admin') {
955
+            throw new OCSException('Cannot create subadmins for admin group', 103);
956
+        }
957
+
958
+        $subAdminManager = $this->groupManager->getSubAdmin();
959
+
960
+        // We cannot be subadmin twice
961
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
962
+            return new DataResponse();
963
+        }
964
+        // Go
965
+        $subAdminManager->createSubAdmin($user, $group);
966
+        return new DataResponse();
967
+    }
968
+
969
+    /**
970
+     * Removes a subadmin from a group
971
+     *
972
+     * @PasswordConfirmationRequired
973
+     *
974
+     * @param string $userId
975
+     * @param string $groupid
976
+     * @return DataResponse
977
+     * @throws OCSException
978
+     */
979
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
980
+        $group = $this->groupManager->get($groupid);
981
+        $user = $this->userManager->get($userId);
982
+        $subAdminManager = $this->groupManager->getSubAdmin();
983
+
984
+        // Check if the user exists
985
+        if ($user === null) {
986
+            throw new OCSException('User does not exist', 101);
987
+        }
988
+        // Check if the group exists
989
+        if ($group === null) {
990
+            throw new OCSException('Group does not exist', 101);
991
+        }
992
+        // Check if they are a subadmin of this said group
993
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
994
+            throw new OCSException('User is not a subadmin of this group', 102);
995
+        }
996
+
997
+        // Go
998
+        $subAdminManager->deleteSubAdmin($user, $group);
999
+        return new DataResponse();
1000
+    }
1001
+
1002
+    /**
1003
+     * Get the groups a user is a subadmin of
1004
+     *
1005
+     * @param string $userId
1006
+     * @return DataResponse
1007
+     * @throws OCSException
1008
+     */
1009
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1010
+        $groups = $this->getUserSubAdminGroupsData($userId);
1011
+        return new DataResponse($groups);
1012
+    }
1013
+
1014
+    /**
1015
+     * @NoAdminRequired
1016
+     * @PasswordConfirmationRequired
1017
+     *
1018
+     * resend welcome message
1019
+     *
1020
+     * @param string $userId
1021
+     * @return DataResponse
1022
+     * @throws OCSException
1023
+     */
1024
+    public function resendWelcomeMessage(string $userId): DataResponse {
1025
+        $currentLoggedInUser = $this->userSession->getUser();
1026
+
1027
+        $targetUser = $this->userManager->get($userId);
1028
+        if ($targetUser === null) {
1029
+            throw new OCSException('', \OCP\API::RESPOND_NOT_FOUND);
1030
+        }
1031
+
1032
+        // Check if admin / subadmin
1033
+        $subAdminManager = $this->groupManager->getSubAdmin();
1034
+        if (!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1035
+            && !$this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
1036
+            // No rights
1037
+            throw new OCSException('', \OCP\API::RESPOND_UNAUTHORISED);
1038
+        }
1039
+
1040
+        $email = $targetUser->getEMailAddress();
1041
+        if ($email === '' || $email === null) {
1042
+            throw new OCSException('Email address not available', 101);
1043
+        }
1044
+
1045
+        try {
1046
+            $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1047
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1048
+        } catch (\Exception $e) {
1049
+            $this->logger->logException($e, [
1050
+                'message' => "Can't send new user mail to $email",
1051
+                'level' => ILogger::ERROR,
1052
+                'app' => 'settings',
1053
+            ]);
1054
+            throw new OCSException('Sending email failed', 102);
1055
+        }
1056
+
1057
+        return new DataResponse();
1058
+    }
1059 1059
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 		$matches = [];
266 266
 		foreach ($userMatches as $phone => $userId) {
267 267
 			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
268
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
268
+			$matches[$normalizedNumberToKey[$phone]] = $userId.'@'.$cloudUrl;
269 269
 		}
270 270
 
271 271
 		return new DataResponse($matches);
@@ -328,7 +328,7 @@  discard block
 block discarded – undo
328 328
 					throw new OCSException('group '.$group.' does not exist', 104);
329 329
 				}
330 330
 				if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
331
-					throw new OCSException('insufficient privileges for group '. $group, 105);
331
+					throw new OCSException('insufficient privileges for group '.$group, 105);
332 332
 				}
333 333
 			}
334 334
 		} else {
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 				$group = $this->groupManager->get($groupid);
344 344
 				// Check if group exists
345 345
 				if ($group === null) {
346
-					throw new OCSException('Subadmin group does not exist',  102);
346
+					throw new OCSException('Subadmin group does not exist', 102);
347 347
 				}
348 348
 				// Check if trying to make subadmin of admin group
349 349
 				if ($group->getGID() === 'admin') {
@@ -384,11 +384,11 @@  discard block
 block discarded – undo
384 384
 
385 385
 		try {
386 386
 			$newUser = $this->userManager->createUser($userid, $password);
387
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
387
+			$this->logger->info('Successful addUser call with userid: '.$userid, ['app' => 'ocs_api']);
388 388
 
389 389
 			foreach ($groups as $group) {
390 390
 				$this->groupManager->get($group)->addUser($newUser);
391
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
391
+				$this->logger->info('Added userid '.$userid.' to group '.$group, ['app' => 'ocs_api']);
392 392
 			}
393 393
 			foreach ($subadminGroups as $group) {
394 394
 				$subAdminManager->createSubAdmin($newUser, $group);
@@ -664,7 +664,7 @@  discard block
 block discarded – undo
664 664
 					try {
665 665
 						$this->accountManager->updateUser($targetUser, $userAccount, true);
666 666
 					} catch (\InvalidArgumentException $e) {
667
-						throw new OCSException('Invalid ' . $e->getMessage(), 102);
667
+						throw new OCSException('Invalid '.$e->getMessage(), 102);
668 668
 					}
669 669
 				}
670 670
 				break;
@@ -911,7 +911,7 @@  discard block
 block discarded – undo
911 911
 		} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
912 912
 			/** @var IGroup[] $subAdminGroups */
913 913
 			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
914
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
914
+			$subAdminGroups = array_map(function(IGroup $subAdminGroup) {
915 915
 				return $subAdminGroup->getGID();
916 916
 			}, $subAdminGroups);
917 917
 			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
@@ -948,7 +948,7 @@  discard block
 block discarded – undo
948 948
 		}
949 949
 		// Check if group exists
950 950
 		if ($group === null) {
951
-			throw new OCSException('Group does not exist',  102);
951
+			throw new OCSException('Group does not exist', 102);
952 952
 		}
953 953
 		// Check if trying to make subadmin of admin group
954 954
 		if ($group->getGID() === 'admin') {
Please login to merge, or discard this patch.
apps/settings/templates/settings/personal/personal.info.php 1 patch
Indentation   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -26,11 +26,11 @@  discard block
 block discarded – undo
26 26
 /** @var array $_ */
27 27
 
28 28
 script('settings', [
29
-	'usersettings',
30
-	'templates',
31
-	'federationsettingsview',
32
-	'federationscopemenu',
33
-	'settings/personalInfo',
29
+    'usersettings',
30
+    'templates',
31
+    'federationsettingsview',
32
+    'federationscopemenu',
33
+    'settings/personalInfo',
34 34
 ]);
35 35
 ?>
36 36
 
@@ -86,10 +86,10 @@  discard block
 block discarded – undo
86 86
 					<p class="quotatext">
87 87
 						<?php if ($_['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED): ?>
88 88
 							<?php print_unescaped($l->t('You are using <strong>%s</strong>',
89
-								[$_['usage']]));?>
89
+                                [$_['usage']]));?>
90 90
 						<?php else: ?>
91 91
 							<?php print_unescaped($l->t('You are using <strong>%1$s</strong> of <strong>%2$s</strong> (<strong>%3$s %%</strong>)',
92
-								[$_['usage'], $_['total_space'],  $_['usage_relative']]));?>
92
+                                [$_['usage'], $_['total_space'],  $_['usage_relative']]));?>
93 93
 						<?php endif ?>
94 94
 					</p>
95 95
 				</div>
@@ -111,16 +111,16 @@  discard block
 block discarded – undo
111 111
 				</h3>
112 112
 				<input type="text" id="displayname" name="displayname"
113 113
 					<?php if (!$_['displayNameChangeSupported']) {
114
-									print_unescaped('class="hidden"');
115
-								} ?>
114
+                                    print_unescaped('class="hidden"');
115
+                                } ?>
116 116
 					   value="<?php p($_['displayName']) ?>"
117 117
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
118 118
 				<?php if (!$_['displayNameChangeSupported']) { ?>
119 119
 					<span><?php if (isset($_['displayName']) && !empty($_['displayName'])) {
120
-									p($_['displayName']);
121
-								} else {
122
-									p($l->t('No display name set'));
123
-								} ?></span>
120
+                                    p($_['displayName']);
121
+                                } else {
122
+                                    p($l->t('No display name set'));
123
+                                } ?></span>
124 124
 				<?php } ?>
125 125
 				<span class="icon-checkmark hidden"></span>
126 126
 				<span class="icon-error hidden" ></span>
@@ -140,36 +140,36 @@  discard block
 block discarded – undo
140 140
 					</div>
141 141
 				</h3>
142 142
 				<div class="verify <?php if ($_['email'] === '' || $_['emailScope'] !== 'public') {
143
-									p('hidden');
144
-								} ?>">
143
+                                    p('hidden');
144
+                                } ?>">
145 145
 					<img id="verify-email" title="<?php p($_['emailMessage']); ?>" data-status="<?php p($_['emailVerification']) ?>" src="
146 146
 				<?php
147
-					switch ($_['emailVerification']) {
148
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
149
-							p(image_path('core', 'actions/verifying.svg'));
150
-							break;
151
-						case \OC\Accounts\AccountManager::VERIFIED:
152
-							p(image_path('core', 'actions/verified.svg'));
153
-							break;
154
-						default:
155
-							p(image_path('core', 'actions/verify.svg'));
156
-					}
157
-					?>">
147
+                    switch ($_['emailVerification']) {
148
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
149
+                            p(image_path('core', 'actions/verifying.svg'));
150
+                            break;
151
+                        case \OC\Accounts\AccountManager::VERIFIED:
152
+                            p(image_path('core', 'actions/verified.svg'));
153
+                            break;
154
+                        default:
155
+                            p(image_path('core', 'actions/verify.svg'));
156
+                    }
157
+                    ?>">
158 158
 				</div>
159 159
 				<input type="email" name="email" id="email" value="<?php p($_['email']); ?>"
160 160
 					<?php if (!$_['displayNameChangeSupported']) {
161
-						print_unescaped('class="hidden"');
162
-					} ?>
161
+                        print_unescaped('class="hidden"');
162
+                    } ?>
163 163
 					   placeholder="<?php p($l->t('Your email address')); ?>"
164 164
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
165 165
 				<span class="icon-checkmark hidden"></span>
166 166
 				<span class="icon-error hidden" ></span>
167 167
 				<?php if (!$_['displayNameChangeSupported']) { ?>
168 168
 					<span><?php if (isset($_['email']) && !empty($_['email'])) {
169
-						p($_['email']);
170
-					} else {
171
-						p($l->t('No email address set'));
172
-					}?></span>
169
+                        p($_['email']);
170
+                    } else {
171
+                        p($l->t('No email address set'));
172
+                    }?></span>
173 173
 				<?php } ?>
174 174
 				<?php if ($_['displayNameChangeSupported']) { ?>
175 175
 					<em><?php p($l->t('For password reset and notifications')); ?></em>
@@ -191,8 +191,8 @@  discard block
 block discarded – undo
191 191
 					</div>
192 192
 				</h3>
193 193
 				<input type="tel" id="phone" name="phone" <?php if (!$_['lookupServerUploadEnabled']) {
194
-						print_unescaped('disabled="1"');
195
-					} ?>
194
+                        print_unescaped('disabled="1"');
195
+                    } ?>
196 196
 					   value="<?php p($_['phone']) ?>"
197 197
 					   placeholder="<?php p($l->t('Your phone number')); ?>"
198 198
 				       autocomplete="on" autocapitalize="none" autocorrect="off" />
@@ -216,8 +216,8 @@  discard block
 block discarded – undo
216 216
 					</div>
217 217
 				</h3>
218 218
 				<input type="text" id="address" name="address" <?php if (!$_['lookupServerUploadEnabled']) {
219
-						print_unescaped('disabled="1"');
220
-					}  ?>
219
+                        print_unescaped('disabled="1"');
220
+                    }  ?>
221 221
 					   placeholder="<?php p($l->t('Your postal address')); ?>"
222 222
 					   value="<?php p($_['address']) ?>"
223 223
 					   autocomplete="on" autocapitalize="none" autocorrect="off" />
@@ -242,24 +242,24 @@  discard block
 block discarded – undo
242 242
 				</h3>
243 243
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
244 244
 				<div class="verify <?php if ($_['website'] === '' || $_['websiteScope'] !== 'public') {
245
-						p('hidden');
246
-					} ?>">
245
+                        p('hidden');
246
+                    } ?>">
247 247
 					<img id="verify-website" title="<?php p($_['websiteMessage']); ?>" data-status="<?php p($_['websiteVerification']) ?>" src="
248 248
 					<?php
249
-					switch ($_['websiteVerification']) {
250
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
251
-							p(image_path('core', 'actions/verifying.svg'));
252
-							break;
253
-						case \OC\Accounts\AccountManager::VERIFIED:
254
-							p(image_path('core', 'actions/verified.svg'));
255
-							break;
256
-						default:
257
-							p(image_path('core', 'actions/verify.svg'));
258
-					}
259
-					?>"
249
+                    switch ($_['websiteVerification']) {
250
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
251
+                            p(image_path('core', 'actions/verifying.svg'));
252
+                            break;
253
+                        case \OC\Accounts\AccountManager::VERIFIED:
254
+                            p(image_path('core', 'actions/verified.svg'));
255
+                            break;
256
+                        default:
257
+                            p(image_path('core', 'actions/verify.svg'));
258
+                    }
259
+                    ?>"
260 260
 					<?php if ($_['websiteVerification'] === \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS || $_['websiteVerification'] === \OC\Accounts\AccountManager::NOT_VERIFIED) {
261
-						print_unescaped(' class="verify-action"');
262
-					} ?>
261
+                        print_unescaped(' class="verify-action"');
262
+                    } ?>
263 263
 					>
264 264
 					<div class="verification-dialog popovermenu bubble menu">
265 265
 						<div class="verification-dialog-content">
@@ -274,8 +274,8 @@  discard block
 block discarded – undo
274 274
 				       placeholder="<?php p($l->t('Link https://…')); ?>"
275 275
 				       autocomplete="on" autocapitalize="none" autocorrect="off"
276 276
 					   <?php if (!$_['lookupServerUploadEnabled']) {
277
-						print_unescaped('disabled="1"');
278
-					}  ?>
277
+                        print_unescaped('disabled="1"');
278
+                    }  ?>
279 279
 				/>
280 280
 				<span class="icon-checkmark hidden"></span>
281 281
 				<span class="icon-error hidden" ></span>
@@ -298,24 +298,24 @@  discard block
 block discarded – undo
298 298
 				</h3>
299 299
 				<?php if ($_['lookupServerUploadEnabled']) { ?>
300 300
 				<div class="verify <?php if ($_['twitter'] === '' || $_['twitterScope'] !== 'public') {
301
-						p('hidden');
302
-					} ?>">
301
+                        p('hidden');
302
+                    } ?>">
303 303
 					<img id="verify-twitter" title="<?php p($_['twitterMessage']); ?>" data-status="<?php p($_['twitterVerification']) ?>" src="
304 304
 					<?php
305
-					switch ($_['twitterVerification']) {
306
-						case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
307
-							p(image_path('core', 'actions/verifying.svg'));
308
-							break;
309
-						case \OC\Accounts\AccountManager::VERIFIED:
310
-							p(image_path('core', 'actions/verified.svg'));
311
-							break;
312
-						default:
313
-							p(image_path('core', 'actions/verify.svg'));
314
-					}
315
-					?>"
305
+                    switch ($_['twitterVerification']) {
306
+                        case \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS:
307
+                            p(image_path('core', 'actions/verifying.svg'));
308
+                            break;
309
+                        case \OC\Accounts\AccountManager::VERIFIED:
310
+                            p(image_path('core', 'actions/verified.svg'));
311
+                            break;
312
+                        default:
313
+                            p(image_path('core', 'actions/verify.svg'));
314
+                    }
315
+                    ?>"
316 316
 					<?php if ($_['twitterVerification'] === \OC\Accounts\AccountManager::VERIFICATION_IN_PROGRESS || $_['twitterVerification'] === \OC\Accounts\AccountManager::NOT_VERIFIED) {
317
-						print_unescaped(' class="verify-action"');
318
-					} ?>
317
+                        print_unescaped(' class="verify-action"');
318
+                    } ?>
319 319
 					>
320 320
 					<div class="verification-dialog popovermenu bubble menu">
321 321
 						<div class="verification-dialog-content">
@@ -330,8 +330,8 @@  discard block
 block discarded – undo
330 330
 					   placeholder="<?php p($l->t('Twitter handle @…')); ?>"
331 331
 					   autocomplete="on" autocapitalize="none" autocorrect="off"
332 332
 					   <?php if (!$_['lookupServerUploadEnabled']) {
333
-						print_unescaped('disabled="1"');
334
-					}  ?>
333
+                        print_unescaped('disabled="1"');
334
+                    }  ?>
335 335
 				/>
336 336
 				<span class="icon-checkmark hidden"></span>
337 337
 				<span class="icon-error hidden" ></span>
Please login to merge, or discard this patch.
apps/settings/lib/BackgroundJobs/VerifyUserData.php 2 patches
Indentation   +253 added lines, -253 removed lines patch added patch discarded remove patch
@@ -41,257 +41,257 @@
 block discarded – undo
41 41
 
42 42
 class VerifyUserData extends Job {
43 43
 
44
-	/** @var  bool */
45
-	private $retainJob = true;
46
-
47
-	/** @var int max number of attempts to send the request */
48
-	private $maxTry = 24;
49
-
50
-	/** @var int how much time should be between two tries (1 hour) */
51
-	private $interval = 3600;
52
-
53
-	/** @var AccountManager */
54
-	private $accountManager;
55
-
56
-	/** @var IUserManager */
57
-	private $userManager;
58
-
59
-	/** @var IClientService */
60
-	private $httpClientService;
61
-
62
-	/** @var ILogger */
63
-	private $logger;
64
-
65
-	/** @var string */
66
-	private $lookupServerUrl;
67
-
68
-	/** @var IConfig */
69
-	private $config;
70
-
71
-	/**
72
-	 * VerifyUserData constructor.
73
-	 *
74
-	 * @param AccountManager $accountManager
75
-	 * @param IUserManager $userManager
76
-	 * @param IClientService $clientService
77
-	 * @param ILogger $logger
78
-	 * @param IConfig $config
79
-	 */
80
-	public function __construct(AccountManager $accountManager,
81
-								IUserManager $userManager,
82
-								IClientService $clientService,
83
-								ILogger $logger,
84
-								IConfig $config
85
-	) {
86
-		$this->accountManager = $accountManager;
87
-		$this->userManager = $userManager;
88
-		$this->httpClientService = $clientService;
89
-		$this->logger = $logger;
90
-
91
-		$lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
92
-		$this->lookupServerUrl = rtrim($lookupServerUrl, '/');
93
-		$this->config = $config;
94
-	}
95
-
96
-	/**
97
-	 * run the job, then remove it from the jobList
98
-	 *
99
-	 * @param IJobList $jobList
100
-	 * @param ILogger|null $logger
101
-	 */
102
-	public function execute(IJobList $jobList, ILogger $logger = null) {
103
-		if ($this->shouldRun($this->argument)) {
104
-			parent::execute($jobList, $logger);
105
-			$jobList->remove($this, $this->argument);
106
-			if ($this->retainJob) {
107
-				$this->reAddJob($jobList, $this->argument);
108
-			} else {
109
-				$this->resetVerificationState();
110
-			}
111
-		}
112
-	}
113
-
114
-	protected function run($argument) {
115
-		$try = (int)$argument['try'] + 1;
116
-
117
-		switch ($argument['type']) {
118
-			case IAccountManager::PROPERTY_WEBSITE:
119
-				$result = $this->verifyWebsite($argument);
120
-				break;
121
-			case IAccountManager::PROPERTY_TWITTER:
122
-			case IAccountManager::PROPERTY_EMAIL:
123
-				$result = $this->verifyViaLookupServer($argument, $argument['type']);
124
-				break;
125
-			default:
126
-				// no valid type given, no need to retry
127
-				$this->logger->error($argument['type'] . ' is no valid type for user account data.');
128
-				$result = true;
129
-		}
130
-
131
-		if ($result === true || $try > $this->maxTry) {
132
-			$this->retainJob = false;
133
-		}
134
-	}
135
-
136
-	/**
137
-	 * verify web page
138
-	 *
139
-	 * @param array $argument
140
-	 * @return bool true if we could check the verification code, otherwise false
141
-	 */
142
-	protected function verifyWebsite(array $argument) {
143
-		$result = false;
144
-
145
-		$url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
146
-
147
-		$client = $this->httpClientService->newClient();
148
-		try {
149
-			$response = $client->get($url);
150
-		} catch (\Exception $e) {
151
-			return false;
152
-		}
153
-
154
-		if ($response->getStatusCode() === Http::STATUS_OK) {
155
-			$result = true;
156
-			$publishedCode = $response->getBody();
157
-			// remove new lines and spaces
158
-			$publishedCodeSanitized = trim(preg_replace('/\s\s+/', ' ', $publishedCode));
159
-			$user = $this->userManager->get($argument['uid']);
160
-			// we don't check a valid user -> give up
161
-			if ($user === null) {
162
-				$this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
163
-				return $result;
164
-			}
165
-			$userData = $this->accountManager->getUser($user);
166
-
167
-			if ($publishedCodeSanitized === $argument['verificationCode']) {
168
-				$userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
169
-			} else {
170
-				$userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
171
-			}
172
-
173
-			$this->accountManager->updateUser($user, $userData);
174
-		}
175
-
176
-		return $result;
177
-	}
178
-
179
-	/**
180
-	 * verify email address
181
-	 *
182
-	 * @param array $argument
183
-	 * @param string $dataType
184
-	 * @return bool true if we could check the verification code, otherwise false
185
-	 */
186
-	protected function verifyViaLookupServer(array $argument, $dataType) {
187
-		if (empty($this->lookupServerUrl) ||
188
-			$this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
189
-			$this->config->getSystemValue('has_internet_connection', true) === false) {
190
-			return false;
191
-		}
192
-
193
-		$user = $this->userManager->get($argument['uid']);
194
-
195
-		// we don't check a valid user -> give up
196
-		if ($user === null) {
197
-			$this->logger->info($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
198
-			return true;
199
-		}
200
-
201
-		$localUserData = $this->accountManager->getUser($user);
202
-		$cloudId = $user->getCloudId();
203
-
204
-		// ask lookup-server for user data
205
-		$lookupServerData = $this->queryLookupServer($cloudId);
206
-
207
-		// for some reasons we couldn't read any data from the lookup server, try again later
208
-		if (empty($lookupServerData) || empty($lookupServerData[$dataType])) {
209
-			return false;
210
-		}
211
-
212
-		// lookup server has verification data for wrong user data (e.g. email address), try again later
213
-		if ($lookupServerData[$dataType]['value'] !== $argument['data']) {
214
-			return false;
215
-		}
216
-
217
-		// lookup server hasn't verified the email address so far, try again later
218
-		if ($lookupServerData[$dataType]['verified'] === AccountManager::NOT_VERIFIED) {
219
-			return false;
220
-		}
221
-
222
-		$localUserData[$dataType]['verified'] = AccountManager::VERIFIED;
223
-		$this->accountManager->updateUser($user, $localUserData);
224
-
225
-		return true;
226
-	}
227
-
228
-	/**
229
-	 * @param string $cloudId
230
-	 * @return array
231
-	 */
232
-	protected function queryLookupServer($cloudId) {
233
-		try {
234
-			$client = $this->httpClientService->newClient();
235
-			$response = $client->get(
236
-				$this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
237
-				[
238
-					'timeout' => 10,
239
-					'connect_timeout' => 3,
240
-				]
241
-			);
242
-
243
-			$body = json_decode($response->getBody(), true);
244
-
245
-			if (is_array($body) && isset($body['federationId']) && $body['federationId'] === $cloudId) {
246
-				return $body;
247
-			}
248
-		} catch (\Exception $e) {
249
-			// do nothing, we will just re-try later
250
-		}
251
-
252
-		return [];
253
-	}
254
-
255
-	/**
256
-	 * re-add background job with new arguments
257
-	 *
258
-	 * @param IJobList $jobList
259
-	 * @param array $argument
260
-	 */
261
-	protected function reAddJob(IJobList $jobList, array $argument) {
262
-		$jobList->add(VerifyUserData::class,
263
-			[
264
-				'verificationCode' => $argument['verificationCode'],
265
-				'data' => $argument['data'],
266
-				'type' => $argument['type'],
267
-				'uid' => $argument['uid'],
268
-				'try' => (int)$argument['try'] + 1,
269
-				'lastRun' => time()
270
-			]
271
-		);
272
-	}
273
-
274
-	/**
275
-	 * test if it is time for the next run
276
-	 *
277
-	 * @param array $argument
278
-	 * @return bool
279
-	 */
280
-	protected function shouldRun(array $argument) {
281
-		$lastRun = (int)$argument['lastRun'];
282
-		return ((time() - $lastRun) > $this->interval);
283
-	}
284
-
285
-
286
-	/**
287
-	 * reset verification state after max tries are reached
288
-	 */
289
-	protected function resetVerificationState() {
290
-		$user = $this->userManager->get($this->argument['uid']);
291
-		if ($user !== null) {
292
-			$accountData = $this->accountManager->getUser($user);
293
-			$accountData[$this->argument['type']]['verified'] = AccountManager::NOT_VERIFIED;
294
-			$this->accountManager->updateUser($user, $accountData);
295
-		}
296
-	}
44
+    /** @var  bool */
45
+    private $retainJob = true;
46
+
47
+    /** @var int max number of attempts to send the request */
48
+    private $maxTry = 24;
49
+
50
+    /** @var int how much time should be between two tries (1 hour) */
51
+    private $interval = 3600;
52
+
53
+    /** @var AccountManager */
54
+    private $accountManager;
55
+
56
+    /** @var IUserManager */
57
+    private $userManager;
58
+
59
+    /** @var IClientService */
60
+    private $httpClientService;
61
+
62
+    /** @var ILogger */
63
+    private $logger;
64
+
65
+    /** @var string */
66
+    private $lookupServerUrl;
67
+
68
+    /** @var IConfig */
69
+    private $config;
70
+
71
+    /**
72
+     * VerifyUserData constructor.
73
+     *
74
+     * @param AccountManager $accountManager
75
+     * @param IUserManager $userManager
76
+     * @param IClientService $clientService
77
+     * @param ILogger $logger
78
+     * @param IConfig $config
79
+     */
80
+    public function __construct(AccountManager $accountManager,
81
+                                IUserManager $userManager,
82
+                                IClientService $clientService,
83
+                                ILogger $logger,
84
+                                IConfig $config
85
+    ) {
86
+        $this->accountManager = $accountManager;
87
+        $this->userManager = $userManager;
88
+        $this->httpClientService = $clientService;
89
+        $this->logger = $logger;
90
+
91
+        $lookupServerUrl = $config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com');
92
+        $this->lookupServerUrl = rtrim($lookupServerUrl, '/');
93
+        $this->config = $config;
94
+    }
95
+
96
+    /**
97
+     * run the job, then remove it from the jobList
98
+     *
99
+     * @param IJobList $jobList
100
+     * @param ILogger|null $logger
101
+     */
102
+    public function execute(IJobList $jobList, ILogger $logger = null) {
103
+        if ($this->shouldRun($this->argument)) {
104
+            parent::execute($jobList, $logger);
105
+            $jobList->remove($this, $this->argument);
106
+            if ($this->retainJob) {
107
+                $this->reAddJob($jobList, $this->argument);
108
+            } else {
109
+                $this->resetVerificationState();
110
+            }
111
+        }
112
+    }
113
+
114
+    protected function run($argument) {
115
+        $try = (int)$argument['try'] + 1;
116
+
117
+        switch ($argument['type']) {
118
+            case IAccountManager::PROPERTY_WEBSITE:
119
+                $result = $this->verifyWebsite($argument);
120
+                break;
121
+            case IAccountManager::PROPERTY_TWITTER:
122
+            case IAccountManager::PROPERTY_EMAIL:
123
+                $result = $this->verifyViaLookupServer($argument, $argument['type']);
124
+                break;
125
+            default:
126
+                // no valid type given, no need to retry
127
+                $this->logger->error($argument['type'] . ' is no valid type for user account data.');
128
+                $result = true;
129
+        }
130
+
131
+        if ($result === true || $try > $this->maxTry) {
132
+            $this->retainJob = false;
133
+        }
134
+    }
135
+
136
+    /**
137
+     * verify web page
138
+     *
139
+     * @param array $argument
140
+     * @return bool true if we could check the verification code, otherwise false
141
+     */
142
+    protected function verifyWebsite(array $argument) {
143
+        $result = false;
144
+
145
+        $url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
146
+
147
+        $client = $this->httpClientService->newClient();
148
+        try {
149
+            $response = $client->get($url);
150
+        } catch (\Exception $e) {
151
+            return false;
152
+        }
153
+
154
+        if ($response->getStatusCode() === Http::STATUS_OK) {
155
+            $result = true;
156
+            $publishedCode = $response->getBody();
157
+            // remove new lines and spaces
158
+            $publishedCodeSanitized = trim(preg_replace('/\s\s+/', ' ', $publishedCode));
159
+            $user = $this->userManager->get($argument['uid']);
160
+            // we don't check a valid user -> give up
161
+            if ($user === null) {
162
+                $this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
163
+                return $result;
164
+            }
165
+            $userData = $this->accountManager->getUser($user);
166
+
167
+            if ($publishedCodeSanitized === $argument['verificationCode']) {
168
+                $userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFIED;
169
+            } else {
170
+                $userData[IAccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::NOT_VERIFIED;
171
+            }
172
+
173
+            $this->accountManager->updateUser($user, $userData);
174
+        }
175
+
176
+        return $result;
177
+    }
178
+
179
+    /**
180
+     * verify email address
181
+     *
182
+     * @param array $argument
183
+     * @param string $dataType
184
+     * @return bool true if we could check the verification code, otherwise false
185
+     */
186
+    protected function verifyViaLookupServer(array $argument, $dataType) {
187
+        if (empty($this->lookupServerUrl) ||
188
+            $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes') !== 'yes' ||
189
+            $this->config->getSystemValue('has_internet_connection', true) === false) {
190
+            return false;
191
+        }
192
+
193
+        $user = $this->userManager->get($argument['uid']);
194
+
195
+        // we don't check a valid user -> give up
196
+        if ($user === null) {
197
+            $this->logger->info($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
198
+            return true;
199
+        }
200
+
201
+        $localUserData = $this->accountManager->getUser($user);
202
+        $cloudId = $user->getCloudId();
203
+
204
+        // ask lookup-server for user data
205
+        $lookupServerData = $this->queryLookupServer($cloudId);
206
+
207
+        // for some reasons we couldn't read any data from the lookup server, try again later
208
+        if (empty($lookupServerData) || empty($lookupServerData[$dataType])) {
209
+            return false;
210
+        }
211
+
212
+        // lookup server has verification data for wrong user data (e.g. email address), try again later
213
+        if ($lookupServerData[$dataType]['value'] !== $argument['data']) {
214
+            return false;
215
+        }
216
+
217
+        // lookup server hasn't verified the email address so far, try again later
218
+        if ($lookupServerData[$dataType]['verified'] === AccountManager::NOT_VERIFIED) {
219
+            return false;
220
+        }
221
+
222
+        $localUserData[$dataType]['verified'] = AccountManager::VERIFIED;
223
+        $this->accountManager->updateUser($user, $localUserData);
224
+
225
+        return true;
226
+    }
227
+
228
+    /**
229
+     * @param string $cloudId
230
+     * @return array
231
+     */
232
+    protected function queryLookupServer($cloudId) {
233
+        try {
234
+            $client = $this->httpClientService->newClient();
235
+            $response = $client->get(
236
+                $this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
237
+                [
238
+                    'timeout' => 10,
239
+                    'connect_timeout' => 3,
240
+                ]
241
+            );
242
+
243
+            $body = json_decode($response->getBody(), true);
244
+
245
+            if (is_array($body) && isset($body['federationId']) && $body['federationId'] === $cloudId) {
246
+                return $body;
247
+            }
248
+        } catch (\Exception $e) {
249
+            // do nothing, we will just re-try later
250
+        }
251
+
252
+        return [];
253
+    }
254
+
255
+    /**
256
+     * re-add background job with new arguments
257
+     *
258
+     * @param IJobList $jobList
259
+     * @param array $argument
260
+     */
261
+    protected function reAddJob(IJobList $jobList, array $argument) {
262
+        $jobList->add(VerifyUserData::class,
263
+            [
264
+                'verificationCode' => $argument['verificationCode'],
265
+                'data' => $argument['data'],
266
+                'type' => $argument['type'],
267
+                'uid' => $argument['uid'],
268
+                'try' => (int)$argument['try'] + 1,
269
+                'lastRun' => time()
270
+            ]
271
+        );
272
+    }
273
+
274
+    /**
275
+     * test if it is time for the next run
276
+     *
277
+     * @param array $argument
278
+     * @return bool
279
+     */
280
+    protected function shouldRun(array $argument) {
281
+        $lastRun = (int)$argument['lastRun'];
282
+        return ((time() - $lastRun) > $this->interval);
283
+    }
284
+
285
+
286
+    /**
287
+     * reset verification state after max tries are reached
288
+     */
289
+    protected function resetVerificationState() {
290
+        $user = $this->userManager->get($this->argument['uid']);
291
+        if ($user !== null) {
292
+            $accountData = $this->accountManager->getUser($user);
293
+            $accountData[$this->argument['type']]['verified'] = AccountManager::NOT_VERIFIED;
294
+            $this->accountManager->updateUser($user, $accountData);
295
+        }
296
+    }
297 297
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 	}
113 113
 
114 114
 	protected function run($argument) {
115
-		$try = (int)$argument['try'] + 1;
115
+		$try = (int) $argument['try'] + 1;
116 116
 
117 117
 		switch ($argument['type']) {
118 118
 			case IAccountManager::PROPERTY_WEBSITE:
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
 				break;
125 125
 			default:
126 126
 				// no valid type given, no need to retry
127
-				$this->logger->error($argument['type'] . ' is no valid type for user account data.');
127
+				$this->logger->error($argument['type'].' is no valid type for user account data.');
128 128
 				$result = true;
129 129
 		}
130 130
 
@@ -142,7 +142,7 @@  discard block
 block discarded – undo
142 142
 	protected function verifyWebsite(array $argument) {
143 143
 		$result = false;
144 144
 
145
-		$url = rtrim($argument['data'], '/') . '/.well-known/' . 'CloudIdVerificationCode.txt';
145
+		$url = rtrim($argument['data'], '/').'/.well-known/'.'CloudIdVerificationCode.txt';
146 146
 
147 147
 		$client = $this->httpClientService->newClient();
148 148
 		try {
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 			$user = $this->userManager->get($argument['uid']);
160 160
 			// we don't check a valid user -> give up
161 161
 			if ($user === null) {
162
-				$this->logger->error($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
162
+				$this->logger->error($argument['uid'].' doesn\'t exist, can\'t verify user data.');
163 163
 				return $result;
164 164
 			}
165 165
 			$userData = $this->accountManager->getUser($user);
@@ -194,7 +194,7 @@  discard block
 block discarded – undo
194 194
 
195 195
 		// we don't check a valid user -> give up
196 196
 		if ($user === null) {
197
-			$this->logger->info($argument['uid'] . ' doesn\'t exist, can\'t verify user data.');
197
+			$this->logger->info($argument['uid'].' doesn\'t exist, can\'t verify user data.');
198 198
 			return true;
199 199
 		}
200 200
 
@@ -233,7 +233,7 @@  discard block
 block discarded – undo
233 233
 		try {
234 234
 			$client = $this->httpClientService->newClient();
235 235
 			$response = $client->get(
236
-				$this->lookupServerUrl . '/users?search=' . urlencode($cloudId) . '&exactCloudId=1',
236
+				$this->lookupServerUrl.'/users?search='.urlencode($cloudId).'&exactCloudId=1',
237 237
 				[
238 238
 					'timeout' => 10,
239 239
 					'connect_timeout' => 3,
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
 				'data' => $argument['data'],
266 266
 				'type' => $argument['type'],
267 267
 				'uid' => $argument['uid'],
268
-				'try' => (int)$argument['try'] + 1,
268
+				'try' => (int) $argument['try'] + 1,
269 269
 				'lastRun' => time()
270 270
 			]
271 271
 		);
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
 	 * @return bool
279 279
 	 */
280 280
 	protected function shouldRun(array $argument) {
281
-		$lastRun = (int)$argument['lastRun'];
281
+		$lastRun = (int) $argument['lastRun'];
282 282
 		return ((time() - $lastRun) > $this->interval);
283 283
 	}
284 284
 
Please login to merge, or discard this patch.
apps/settings/lib/Settings/Personal/PersonalInfo.php 2 patches
Indentation   +232 added lines, -232 removed lines patch added patch discarded remove patch
@@ -50,236 +50,236 @@
 block discarded – undo
50 50
 
51 51
 class PersonalInfo implements ISettings {
52 52
 
53
-	/** @var IConfig */
54
-	private $config;
55
-	/** @var IUserManager */
56
-	private $userManager;
57
-	/** @var AccountManager */
58
-	private $accountManager;
59
-	/** @var IGroupManager */
60
-	private $groupManager;
61
-	/** @var IAppManager */
62
-	private $appManager;
63
-	/** @var IFactory */
64
-	private $l10nFactory;
65
-	/** @var IL10N */
66
-	private $l;
67
-
68
-	public function __construct(
69
-		IConfig $config,
70
-		IUserManager $userManager,
71
-		IGroupManager $groupManager,
72
-		AccountManager $accountManager,
73
-		IAppManager $appManager,
74
-		IFactory $l10nFactory,
75
-		IL10N $l
76
-	) {
77
-		$this->config = $config;
78
-		$this->userManager = $userManager;
79
-		$this->accountManager = $accountManager;
80
-		$this->groupManager = $groupManager;
81
-		$this->appManager = $appManager;
82
-		$this->l10nFactory = $l10nFactory;
83
-		$this->l = $l;
84
-	}
85
-
86
-	public function getForm(): TemplateResponse {
87
-		$federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
88
-		$lookupServerUploadEnabled = false;
89
-		if ($federatedFileSharingEnabled) {
90
-			/** @var FederatedShareProvider $shareProvider */
91
-			$shareProvider = \OC::$server->query(FederatedShareProvider::class);
92
-			$lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
93
-		}
94
-
95
-		$uid = \OC_User::getUser();
96
-		$user = $this->userManager->get($uid);
97
-		$userData = $this->accountManager->getUser($user);
98
-
99
-		// make sure FS is setup before querying storage related stuff...
100
-		\OC_Util::setupFS($user->getUID());
101
-
102
-		$storageInfo = \OC_Helper::getStorageInfo('/');
103
-		if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
104
-			$totalSpace = $this->l->t('Unlimited');
105
-		} else {
106
-			$totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
107
-		}
108
-
109
-		$languageParameters = $this->getLanguages($user);
110
-		$localeParameters = $this->getLocales($user);
111
-		$messageParameters = $this->getMessageParameters($userData);
112
-
113
-		$parameters = [
114
-			'total_space' => $totalSpace,
115
-			'usage' => \OC_Helper::humanFileSize($storageInfo['used']),
116
-			'usage_relative' => round($storageInfo['relative']),
117
-			'quota' => $storageInfo['quota'],
118
-			'avatarChangeSupported' => $user->canChangeAvatar(),
119
-			'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
120
-			'avatarScope' => $userData[IAccountManager::PROPERTY_AVATAR]['scope'],
121
-			'displayNameChangeSupported' => $user->canChangeDisplayName(),
122
-			'displayName' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
123
-			'displayNameScope' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
124
-			'email' => $userData[IAccountManager::PROPERTY_EMAIL]['value'],
125
-			'emailScope' => $userData[IAccountManager::PROPERTY_EMAIL]['scope'],
126
-			'emailVerification' => $userData[IAccountManager::PROPERTY_EMAIL]['verified'],
127
-			'phone' => $userData[IAccountManager::PROPERTY_PHONE]['value'],
128
-			'phoneScope' => $userData[IAccountManager::PROPERTY_PHONE]['scope'],
129
-			'address' => $userData[IAccountManager::PROPERTY_ADDRESS]['value'],
130
-			'addressScope' => $userData[IAccountManager::PROPERTY_ADDRESS]['scope'],
131
-			'website' => $userData[IAccountManager::PROPERTY_WEBSITE]['value'],
132
-			'websiteScope' => $userData[IAccountManager::PROPERTY_WEBSITE]['scope'],
133
-			'websiteVerification' => $userData[IAccountManager::PROPERTY_WEBSITE]['verified'],
134
-			'twitter' => $userData[IAccountManager::PROPERTY_TWITTER]['value'],
135
-			'twitterScope' => $userData[IAccountManager::PROPERTY_TWITTER]['scope'],
136
-			'twitterVerification' => $userData[IAccountManager::PROPERTY_TWITTER]['verified'],
137
-			'groups' => $this->getGroups($user),
138
-		] + $messageParameters + $languageParameters + $localeParameters;
139
-
140
-
141
-		return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
142
-	}
143
-
144
-	/**
145
-	 * @return string the section ID, e.g. 'sharing'
146
-	 * @since 9.1
147
-	 */
148
-	public function getSection(): string {
149
-		return 'personal-info';
150
-	}
151
-
152
-	/**
153
-	 * @return int whether the form should be rather on the top or bottom of
154
-	 * the admin section. The forms are arranged in ascending order of the
155
-	 * priority values. It is required to return a value between 0 and 100.
156
-	 *
157
-	 * E.g.: 70
158
-	 * @since 9.1
159
-	 */
160
-	public function getPriority(): int {
161
-		return 10;
162
-	}
163
-
164
-	/**
165
-	 * returns a sorted list of the user's group GIDs
166
-	 *
167
-	 * @param IUser $user
168
-	 * @return array
169
-	 */
170
-	private function getGroups(IUser $user): array {
171
-		$groups = array_map(
172
-			static function (IGroup $group) {
173
-				return $group->getDisplayName();
174
-			},
175
-			$this->groupManager->getUserGroups($user)
176
-		);
177
-		sort($groups);
178
-
179
-		return $groups;
180
-	}
181
-
182
-	/**
183
-	 * returns the user language, common language and other languages in an
184
-	 * associative array
185
-	 *
186
-	 * @param IUser $user
187
-	 * @return array
188
-	 */
189
-	private function getLanguages(IUser $user): array {
190
-		$forceLanguage = $this->config->getSystemValue('force_language', false);
191
-		if ($forceLanguage !== false) {
192
-			return [];
193
-		}
194
-
195
-		$uid = $user->getUID();
196
-
197
-		$userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
198
-		$languages = $this->l10nFactory->getLanguages();
199
-
200
-		// associate the user language with the proper array
201
-		$userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code'));
202
-		$userLang = $languages['commonlanguages'][$userLangIndex];
203
-		// search in the other languages
204
-		if ($userLangIndex === false) {
205
-			$userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code'));
206
-			$userLang = $languages['languages'][$userLangIndex];
207
-		}
208
-		// if user language is not available but set somehow: show the actual code as name
209
-		if (!is_array($userLang)) {
210
-			$userLang = [
211
-				'code' => $userConfLang,
212
-				'name' => $userConfLang,
213
-			];
214
-		}
215
-
216
-		return array_merge(
217
-			['activelanguage' => $userLang],
218
-			$languages
219
-		);
220
-	}
221
-
222
-	private function getLocales(IUser $user): array {
223
-		$forceLanguage = $this->config->getSystemValue('force_locale', false);
224
-		if ($forceLanguage !== false) {
225
-			return [];
226
-		}
227
-
228
-		$uid = $user->getUID();
229
-
230
-		$userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale());
231
-
232
-		$userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
233
-
234
-		$localeCodes = $this->l10nFactory->findAvailableLocales();
235
-
236
-		$userLocale = array_filter($localeCodes, function ($value) use ($userLocaleString) {
237
-			return $userLocaleString === $value['code'];
238
-		});
239
-
240
-		if (!empty($userLocale)) {
241
-			$userLocale = reset($userLocale);
242
-		}
243
-
244
-		$localesForLanguage = array_filter($localeCodes, function ($localeCode) use ($userLang) {
245
-			return 0 === strpos($localeCode['code'], $userLang);
246
-		});
247
-
248
-		if (!$userLocale) {
249
-			$userLocale = [
250
-				'code' => 'en',
251
-				'name' => 'English'
252
-			];
253
-		}
254
-
255
-		return [
256
-			'activelocaleLang' => $userLocaleString,
257
-			'activelocale' => $userLocale,
258
-			'locales' => $localeCodes,
259
-			'localesForLanguage' => $localesForLanguage,
260
-		];
261
-	}
262
-
263
-	/**
264
-	 * @param array $userData
265
-	 * @return array
266
-	 */
267
-	private function getMessageParameters(array $userData): array {
268
-		$needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
269
-		$messageParameters = [];
270
-		foreach ($needVerifyMessage as $property) {
271
-			switch ($userData[$property]['verified']) {
272
-				case AccountManager::VERIFIED:
273
-					$message = $this->l->t('Verifying');
274
-					break;
275
-				case AccountManager::VERIFICATION_IN_PROGRESS:
276
-					$message = $this->l->t('Verifying …');
277
-					break;
278
-				default:
279
-					$message = $this->l->t('Verify');
280
-			}
281
-			$messageParameters[$property . 'Message'] = $message;
282
-		}
283
-		return $messageParameters;
284
-	}
53
+    /** @var IConfig */
54
+    private $config;
55
+    /** @var IUserManager */
56
+    private $userManager;
57
+    /** @var AccountManager */
58
+    private $accountManager;
59
+    /** @var IGroupManager */
60
+    private $groupManager;
61
+    /** @var IAppManager */
62
+    private $appManager;
63
+    /** @var IFactory */
64
+    private $l10nFactory;
65
+    /** @var IL10N */
66
+    private $l;
67
+
68
+    public function __construct(
69
+        IConfig $config,
70
+        IUserManager $userManager,
71
+        IGroupManager $groupManager,
72
+        AccountManager $accountManager,
73
+        IAppManager $appManager,
74
+        IFactory $l10nFactory,
75
+        IL10N $l
76
+    ) {
77
+        $this->config = $config;
78
+        $this->userManager = $userManager;
79
+        $this->accountManager = $accountManager;
80
+        $this->groupManager = $groupManager;
81
+        $this->appManager = $appManager;
82
+        $this->l10nFactory = $l10nFactory;
83
+        $this->l = $l;
84
+    }
85
+
86
+    public function getForm(): TemplateResponse {
87
+        $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing');
88
+        $lookupServerUploadEnabled = false;
89
+        if ($federatedFileSharingEnabled) {
90
+            /** @var FederatedShareProvider $shareProvider */
91
+            $shareProvider = \OC::$server->query(FederatedShareProvider::class);
92
+            $lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled();
93
+        }
94
+
95
+        $uid = \OC_User::getUser();
96
+        $user = $this->userManager->get($uid);
97
+        $userData = $this->accountManager->getUser($user);
98
+
99
+        // make sure FS is setup before querying storage related stuff...
100
+        \OC_Util::setupFS($user->getUID());
101
+
102
+        $storageInfo = \OC_Helper::getStorageInfo('/');
103
+        if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) {
104
+            $totalSpace = $this->l->t('Unlimited');
105
+        } else {
106
+            $totalSpace = \OC_Helper::humanFileSize($storageInfo['total']);
107
+        }
108
+
109
+        $languageParameters = $this->getLanguages($user);
110
+        $localeParameters = $this->getLocales($user);
111
+        $messageParameters = $this->getMessageParameters($userData);
112
+
113
+        $parameters = [
114
+            'total_space' => $totalSpace,
115
+            'usage' => \OC_Helper::humanFileSize($storageInfo['used']),
116
+            'usage_relative' => round($storageInfo['relative']),
117
+            'quota' => $storageInfo['quota'],
118
+            'avatarChangeSupported' => $user->canChangeAvatar(),
119
+            'lookupServerUploadEnabled' => $lookupServerUploadEnabled,
120
+            'avatarScope' => $userData[IAccountManager::PROPERTY_AVATAR]['scope'],
121
+            'displayNameChangeSupported' => $user->canChangeDisplayName(),
122
+            'displayName' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['value'],
123
+            'displayNameScope' => $userData[IAccountManager::PROPERTY_DISPLAYNAME]['scope'],
124
+            'email' => $userData[IAccountManager::PROPERTY_EMAIL]['value'],
125
+            'emailScope' => $userData[IAccountManager::PROPERTY_EMAIL]['scope'],
126
+            'emailVerification' => $userData[IAccountManager::PROPERTY_EMAIL]['verified'],
127
+            'phone' => $userData[IAccountManager::PROPERTY_PHONE]['value'],
128
+            'phoneScope' => $userData[IAccountManager::PROPERTY_PHONE]['scope'],
129
+            'address' => $userData[IAccountManager::PROPERTY_ADDRESS]['value'],
130
+            'addressScope' => $userData[IAccountManager::PROPERTY_ADDRESS]['scope'],
131
+            'website' => $userData[IAccountManager::PROPERTY_WEBSITE]['value'],
132
+            'websiteScope' => $userData[IAccountManager::PROPERTY_WEBSITE]['scope'],
133
+            'websiteVerification' => $userData[IAccountManager::PROPERTY_WEBSITE]['verified'],
134
+            'twitter' => $userData[IAccountManager::PROPERTY_TWITTER]['value'],
135
+            'twitterScope' => $userData[IAccountManager::PROPERTY_TWITTER]['scope'],
136
+            'twitterVerification' => $userData[IAccountManager::PROPERTY_TWITTER]['verified'],
137
+            'groups' => $this->getGroups($user),
138
+        ] + $messageParameters + $languageParameters + $localeParameters;
139
+
140
+
141
+        return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
142
+    }
143
+
144
+    /**
145
+     * @return string the section ID, e.g. 'sharing'
146
+     * @since 9.1
147
+     */
148
+    public function getSection(): string {
149
+        return 'personal-info';
150
+    }
151
+
152
+    /**
153
+     * @return int whether the form should be rather on the top or bottom of
154
+     * the admin section. The forms are arranged in ascending order of the
155
+     * priority values. It is required to return a value between 0 and 100.
156
+     *
157
+     * E.g.: 70
158
+     * @since 9.1
159
+     */
160
+    public function getPriority(): int {
161
+        return 10;
162
+    }
163
+
164
+    /**
165
+     * returns a sorted list of the user's group GIDs
166
+     *
167
+     * @param IUser $user
168
+     * @return array
169
+     */
170
+    private function getGroups(IUser $user): array {
171
+        $groups = array_map(
172
+            static function (IGroup $group) {
173
+                return $group->getDisplayName();
174
+            },
175
+            $this->groupManager->getUserGroups($user)
176
+        );
177
+        sort($groups);
178
+
179
+        return $groups;
180
+    }
181
+
182
+    /**
183
+     * returns the user language, common language and other languages in an
184
+     * associative array
185
+     *
186
+     * @param IUser $user
187
+     * @return array
188
+     */
189
+    private function getLanguages(IUser $user): array {
190
+        $forceLanguage = $this->config->getSystemValue('force_language', false);
191
+        if ($forceLanguage !== false) {
192
+            return [];
193
+        }
194
+
195
+        $uid = $user->getUID();
196
+
197
+        $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
198
+        $languages = $this->l10nFactory->getLanguages();
199
+
200
+        // associate the user language with the proper array
201
+        $userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code'));
202
+        $userLang = $languages['commonlanguages'][$userLangIndex];
203
+        // search in the other languages
204
+        if ($userLangIndex === false) {
205
+            $userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code'));
206
+            $userLang = $languages['languages'][$userLangIndex];
207
+        }
208
+        // if user language is not available but set somehow: show the actual code as name
209
+        if (!is_array($userLang)) {
210
+            $userLang = [
211
+                'code' => $userConfLang,
212
+                'name' => $userConfLang,
213
+            ];
214
+        }
215
+
216
+        return array_merge(
217
+            ['activelanguage' => $userLang],
218
+            $languages
219
+        );
220
+    }
221
+
222
+    private function getLocales(IUser $user): array {
223
+        $forceLanguage = $this->config->getSystemValue('force_locale', false);
224
+        if ($forceLanguage !== false) {
225
+            return [];
226
+        }
227
+
228
+        $uid = $user->getUID();
229
+
230
+        $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale());
231
+
232
+        $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage());
233
+
234
+        $localeCodes = $this->l10nFactory->findAvailableLocales();
235
+
236
+        $userLocale = array_filter($localeCodes, function ($value) use ($userLocaleString) {
237
+            return $userLocaleString === $value['code'];
238
+        });
239
+
240
+        if (!empty($userLocale)) {
241
+            $userLocale = reset($userLocale);
242
+        }
243
+
244
+        $localesForLanguage = array_filter($localeCodes, function ($localeCode) use ($userLang) {
245
+            return 0 === strpos($localeCode['code'], $userLang);
246
+        });
247
+
248
+        if (!$userLocale) {
249
+            $userLocale = [
250
+                'code' => 'en',
251
+                'name' => 'English'
252
+            ];
253
+        }
254
+
255
+        return [
256
+            'activelocaleLang' => $userLocaleString,
257
+            'activelocale' => $userLocale,
258
+            'locales' => $localeCodes,
259
+            'localesForLanguage' => $localesForLanguage,
260
+        ];
261
+    }
262
+
263
+    /**
264
+     * @param array $userData
265
+     * @return array
266
+     */
267
+    private function getMessageParameters(array $userData): array {
268
+        $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER];
269
+        $messageParameters = [];
270
+        foreach ($needVerifyMessage as $property) {
271
+            switch ($userData[$property]['verified']) {
272
+                case AccountManager::VERIFIED:
273
+                    $message = $this->l->t('Verifying');
274
+                    break;
275
+                case AccountManager::VERIFICATION_IN_PROGRESS:
276
+                    $message = $this->l->t('Verifying …');
277
+                    break;
278
+                default:
279
+                    $message = $this->l->t('Verify');
280
+            }
281
+            $messageParameters[$property . 'Message'] = $message;
282
+        }
283
+        return $messageParameters;
284
+    }
285 285
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
 	 */
170 170
 	private function getGroups(IUser $user): array {
171 171
 		$groups = array_map(
172
-			static function (IGroup $group) {
172
+			static function(IGroup $group) {
173 173
 				return $group->getDisplayName();
174 174
 			},
175 175
 			$this->groupManager->getUserGroups($user)
@@ -233,7 +233,7 @@  discard block
 block discarded – undo
233 233
 
234 234
 		$localeCodes = $this->l10nFactory->findAvailableLocales();
235 235
 
236
-		$userLocale = array_filter($localeCodes, function ($value) use ($userLocaleString) {
236
+		$userLocale = array_filter($localeCodes, function($value) use ($userLocaleString) {
237 237
 			return $userLocaleString === $value['code'];
238 238
 		});
239 239
 
@@ -241,7 +241,7 @@  discard block
 block discarded – undo
241 241
 			$userLocale = reset($userLocale);
242 242
 		}
243 243
 
244
-		$localesForLanguage = array_filter($localeCodes, function ($localeCode) use ($userLang) {
244
+		$localesForLanguage = array_filter($localeCodes, function($localeCode) use ($userLang) {
245 245
 			return 0 === strpos($localeCode['code'], $userLang);
246 246
 		});
247 247
 
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
 				default:
279 279
 					$message = $this->l->t('Verify');
280 280
 			}
281
-			$messageParameters[$property . 'Message'] = $message;
281
+			$messageParameters[$property.'Message'] = $message;
282 282
 		}
283 283
 		return $messageParameters;
284 284
 	}
Please login to merge, or discard this patch.
apps/settings/lib/Controller/CheckSetupController.php 1 patch
Indentation   +669 added lines, -669 removed lines patch added patch discarded remove patch
@@ -76,295 +76,295 @@  discard block
 block discarded – undo
76 76
 use Symfony\Component\EventDispatcher\GenericEvent;
77 77
 
78 78
 class CheckSetupController extends Controller {
79
-	/** @var IConfig */
80
-	private $config;
81
-	/** @var IClientService */
82
-	private $clientService;
83
-	/** @var IURLGenerator */
84
-	private $urlGenerator;
85
-	/** @var IL10N */
86
-	private $l10n;
87
-	/** @var Checker */
88
-	private $checker;
89
-	/** @var ILogger */
90
-	private $logger;
91
-	/** @var EventDispatcherInterface */
92
-	private $dispatcher;
93
-	/** @var IDBConnection|Connection */
94
-	private $db;
95
-	/** @var ILockingProvider */
96
-	private $lockingProvider;
97
-	/** @var IDateTimeFormatter */
98
-	private $dateTimeFormatter;
99
-	/** @var MemoryInfo */
100
-	private $memoryInfo;
101
-	/** @var ISecureRandom */
102
-	private $secureRandom;
103
-	/** @var IniGetWrapper */
104
-	private $iniGetWrapper;
105
-
106
-	public function __construct($AppName,
107
-								IRequest $request,
108
-								IConfig $config,
109
-								IClientService $clientService,
110
-								IURLGenerator $urlGenerator,
111
-								IL10N $l10n,
112
-								Checker $checker,
113
-								ILogger $logger,
114
-								EventDispatcherInterface $dispatcher,
115
-								IDBConnection $db,
116
-								ILockingProvider $lockingProvider,
117
-								IDateTimeFormatter $dateTimeFormatter,
118
-								MemoryInfo $memoryInfo,
119
-								ISecureRandom $secureRandom,
120
-								IniGetWrapper $iniGetWrapper) {
121
-		parent::__construct($AppName, $request);
122
-		$this->config = $config;
123
-		$this->clientService = $clientService;
124
-		$this->urlGenerator = $urlGenerator;
125
-		$this->l10n = $l10n;
126
-		$this->checker = $checker;
127
-		$this->logger = $logger;
128
-		$this->dispatcher = $dispatcher;
129
-		$this->db = $db;
130
-		$this->lockingProvider = $lockingProvider;
131
-		$this->dateTimeFormatter = $dateTimeFormatter;
132
-		$this->memoryInfo = $memoryInfo;
133
-		$this->secureRandom = $secureRandom;
134
-		$this->iniGetWrapper = $iniGetWrapper;
135
-	}
136
-
137
-	/**
138
-	 * Checks if the server can connect to the internet using HTTPS and HTTP
139
-	 * @return bool
140
-	 */
141
-	private function hasInternetConnectivityProblems(): bool {
142
-		if ($this->config->getSystemValue('has_internet_connection', true) === false) {
143
-			return false;
144
-		}
145
-
146
-		$siteArray = $this->config->getSystemValue('connectivity_check_domains', [
147
-			'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
148
-		]);
149
-
150
-		foreach ($siteArray as $site) {
151
-			if ($this->isSiteReachable($site)) {
152
-				return false;
153
-			}
154
-		}
155
-		return true;
156
-	}
157
-
158
-	/**
159
-	 * Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
160
-	 * @return bool
161
-	 */
162
-	private function isSiteReachable($sitename) {
163
-		$httpSiteName = 'http://' . $sitename . '/';
164
-		$httpsSiteName = 'https://' . $sitename . '/';
165
-
166
-		try {
167
-			$client = $this->clientService->newClient();
168
-			$client->get($httpSiteName);
169
-			$client->get($httpsSiteName);
170
-		} catch (\Exception $e) {
171
-			$this->logger->logException($e, ['app' => 'internet_connection_check']);
172
-			return false;
173
-		}
174
-		return true;
175
-	}
176
-
177
-	/**
178
-	 * Checks whether a local memcache is installed or not
179
-	 * @return bool
180
-	 */
181
-	private function isMemcacheConfigured() {
182
-		return $this->config->getSystemValue('memcache.local', null) !== null;
183
-	}
184
-
185
-	/**
186
-	 * Whether PHP can generate "secure" pseudorandom integers
187
-	 *
188
-	 * @return bool
189
-	 */
190
-	private function isRandomnessSecure() {
191
-		try {
192
-			$this->secureRandom->generate(1);
193
-		} catch (\Exception $ex) {
194
-			return false;
195
-		}
196
-		return true;
197
-	}
198
-
199
-	/**
200
-	 * Public for the sake of unit-testing
201
-	 *
202
-	 * @return array
203
-	 */
204
-	protected function getCurlVersion() {
205
-		return curl_version();
206
-	}
207
-
208
-	/**
209
-	 * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
210
-	 * have multiple bugs which likely lead to problems in combination with
211
-	 * functionality required by ownCloud such as SNI.
212
-	 *
213
-	 * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
214
-	 * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
215
-	 * @return string
216
-	 */
217
-	private function isUsedTlsLibOutdated() {
218
-		// Don't run check when:
219
-		// 1. Server has `has_internet_connection` set to false
220
-		// 2. AppStore AND S2S is disabled
221
-		if (!$this->config->getSystemValue('has_internet_connection', true)) {
222
-			return '';
223
-		}
224
-		if (!$this->config->getSystemValue('appstoreenabled', true)
225
-			&& $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
226
-			&& $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
227
-			return '';
228
-		}
229
-
230
-		$versionString = $this->getCurlVersion();
231
-		if (isset($versionString['ssl_version'])) {
232
-			$versionString = $versionString['ssl_version'];
233
-		} else {
234
-			return '';
235
-		}
236
-
237
-		$features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
238
-		if (!$this->config->getSystemValue('appstoreenabled', true)) {
239
-			$features = (string)$this->l10n->t('Federated Cloud Sharing');
240
-		}
241
-
242
-		// Check if at least OpenSSL after 1.01d or 1.0.2b
243
-		if (strpos($versionString, 'OpenSSL/') === 0) {
244
-			$majorVersion = substr($versionString, 8, 5);
245
-			$patchRelease = substr($versionString, 13, 6);
246
-
247
-			if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
248
-				($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
249
-				return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
250
-			}
251
-		}
252
-
253
-		// Check if NSS and perform heuristic check
254
-		if (strpos($versionString, 'NSS/') === 0) {
255
-			try {
256
-				$firstClient = $this->clientService->newClient();
257
-				$firstClient->get('https://nextcloud.com/');
258
-
259
-				$secondClient = $this->clientService->newClient();
260
-				$secondClient->get('https://nextcloud.com/');
261
-			} catch (ClientException $e) {
262
-				if ($e->getResponse()->getStatusCode() === 400) {
263
-					return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
264
-				}
265
-			} catch (\Exception $e) {
266
-				$this->logger->logException($e, ['app' => 'settings', 'level' => \OCP\ILogger::WARN]);
267
-				return $this->l10n->t('Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the nextcloud log file for more details.');
268
-			}
269
-		}
270
-
271
-		return '';
272
-	}
273
-
274
-	/**
275
-	 * Whether the version is outdated
276
-	 *
277
-	 * @return bool
278
-	 */
279
-	protected function isPhpOutdated(): bool {
280
-		return PHP_VERSION_ID < 70300;
281
-	}
282
-
283
-	/**
284
-	 * Whether the php version is still supported (at time of release)
285
-	 * according to: https://secure.php.net/supported-versions.php
286
-	 *
287
-	 * @return array
288
-	 */
289
-	private function isPhpSupported(): array {
290
-		return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
291
-	}
292
-
293
-	/**
294
-	 * Check if the reverse proxy configuration is working as expected
295
-	 *
296
-	 * @return bool
297
-	 */
298
-	private function forwardedForHeadersWorking() {
299
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
300
-		$remoteAddress = $this->request->getHeader('REMOTE_ADDR');
301
-
302
-		if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
303
-			return false;
304
-		}
305
-
306
-		if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
307
-			return $remoteAddress !== $this->request->getRemoteAddress();
308
-		}
309
-
310
-		// either not enabled or working correctly
311
-		return true;
312
-	}
313
-
314
-	/**
315
-	 * Checks if the correct memcache module for PHP is installed. Only
316
-	 * fails if memcached is configured and the working module is not installed.
317
-	 *
318
-	 * @return bool
319
-	 */
320
-	private function isCorrectMemcachedPHPModuleInstalled() {
321
-		if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
322
-			return true;
323
-		}
324
-
325
-		// there are two different memcached modules for PHP
326
-		// we only support memcached and not memcache
327
-		// https://code.google.com/p/memcached/wiki/PHPClientComparison
328
-		return !(!extension_loaded('memcached') && extension_loaded('memcache'));
329
-	}
330
-
331
-	/**
332
-	 * Checks if set_time_limit is not disabled.
333
-	 *
334
-	 * @return bool
335
-	 */
336
-	private function isSettimelimitAvailable() {
337
-		if (function_exists('set_time_limit')
338
-			&& strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
339
-			return true;
340
-		}
341
-
342
-		return false;
343
-	}
344
-
345
-	/**
346
-	 * @return RedirectResponse
347
-	 */
348
-	public function rescanFailedIntegrityCheck() {
349
-		$this->checker->runInstanceVerification();
350
-		return new RedirectResponse(
351
-			$this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview'])
352
-		);
353
-	}
354
-
355
-	/**
356
-	 * @NoCSRFRequired
357
-	 * @return DataResponse
358
-	 */
359
-	public function getFailedIntegrityCheckFiles() {
360
-		if (!$this->checker->isCodeCheckEnforced()) {
361
-			return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
362
-		}
363
-
364
-		$completeResults = $this->checker->getResults();
365
-
366
-		if (!empty($completeResults)) {
367
-			$formattedTextResponse = 'Technical information
79
+    /** @var IConfig */
80
+    private $config;
81
+    /** @var IClientService */
82
+    private $clientService;
83
+    /** @var IURLGenerator */
84
+    private $urlGenerator;
85
+    /** @var IL10N */
86
+    private $l10n;
87
+    /** @var Checker */
88
+    private $checker;
89
+    /** @var ILogger */
90
+    private $logger;
91
+    /** @var EventDispatcherInterface */
92
+    private $dispatcher;
93
+    /** @var IDBConnection|Connection */
94
+    private $db;
95
+    /** @var ILockingProvider */
96
+    private $lockingProvider;
97
+    /** @var IDateTimeFormatter */
98
+    private $dateTimeFormatter;
99
+    /** @var MemoryInfo */
100
+    private $memoryInfo;
101
+    /** @var ISecureRandom */
102
+    private $secureRandom;
103
+    /** @var IniGetWrapper */
104
+    private $iniGetWrapper;
105
+
106
+    public function __construct($AppName,
107
+                                IRequest $request,
108
+                                IConfig $config,
109
+                                IClientService $clientService,
110
+                                IURLGenerator $urlGenerator,
111
+                                IL10N $l10n,
112
+                                Checker $checker,
113
+                                ILogger $logger,
114
+                                EventDispatcherInterface $dispatcher,
115
+                                IDBConnection $db,
116
+                                ILockingProvider $lockingProvider,
117
+                                IDateTimeFormatter $dateTimeFormatter,
118
+                                MemoryInfo $memoryInfo,
119
+                                ISecureRandom $secureRandom,
120
+                                IniGetWrapper $iniGetWrapper) {
121
+        parent::__construct($AppName, $request);
122
+        $this->config = $config;
123
+        $this->clientService = $clientService;
124
+        $this->urlGenerator = $urlGenerator;
125
+        $this->l10n = $l10n;
126
+        $this->checker = $checker;
127
+        $this->logger = $logger;
128
+        $this->dispatcher = $dispatcher;
129
+        $this->db = $db;
130
+        $this->lockingProvider = $lockingProvider;
131
+        $this->dateTimeFormatter = $dateTimeFormatter;
132
+        $this->memoryInfo = $memoryInfo;
133
+        $this->secureRandom = $secureRandom;
134
+        $this->iniGetWrapper = $iniGetWrapper;
135
+    }
136
+
137
+    /**
138
+     * Checks if the server can connect to the internet using HTTPS and HTTP
139
+     * @return bool
140
+     */
141
+    private function hasInternetConnectivityProblems(): bool {
142
+        if ($this->config->getSystemValue('has_internet_connection', true) === false) {
143
+            return false;
144
+        }
145
+
146
+        $siteArray = $this->config->getSystemValue('connectivity_check_domains', [
147
+            'www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'
148
+        ]);
149
+
150
+        foreach ($siteArray as $site) {
151
+            if ($this->isSiteReachable($site)) {
152
+                return false;
153
+            }
154
+        }
155
+        return true;
156
+    }
157
+
158
+    /**
159
+     * Checks if the Nextcloud server can connect to a specific URL using both HTTPS and HTTP
160
+     * @return bool
161
+     */
162
+    private function isSiteReachable($sitename) {
163
+        $httpSiteName = 'http://' . $sitename . '/';
164
+        $httpsSiteName = 'https://' . $sitename . '/';
165
+
166
+        try {
167
+            $client = $this->clientService->newClient();
168
+            $client->get($httpSiteName);
169
+            $client->get($httpsSiteName);
170
+        } catch (\Exception $e) {
171
+            $this->logger->logException($e, ['app' => 'internet_connection_check']);
172
+            return false;
173
+        }
174
+        return true;
175
+    }
176
+
177
+    /**
178
+     * Checks whether a local memcache is installed or not
179
+     * @return bool
180
+     */
181
+    private function isMemcacheConfigured() {
182
+        return $this->config->getSystemValue('memcache.local', null) !== null;
183
+    }
184
+
185
+    /**
186
+     * Whether PHP can generate "secure" pseudorandom integers
187
+     *
188
+     * @return bool
189
+     */
190
+    private function isRandomnessSecure() {
191
+        try {
192
+            $this->secureRandom->generate(1);
193
+        } catch (\Exception $ex) {
194
+            return false;
195
+        }
196
+        return true;
197
+    }
198
+
199
+    /**
200
+     * Public for the sake of unit-testing
201
+     *
202
+     * @return array
203
+     */
204
+    protected function getCurlVersion() {
205
+        return curl_version();
206
+    }
207
+
208
+    /**
209
+     * Check if the used  SSL lib is outdated. Older OpenSSL and NSS versions do
210
+     * have multiple bugs which likely lead to problems in combination with
211
+     * functionality required by ownCloud such as SNI.
212
+     *
213
+     * @link https://github.com/owncloud/core/issues/17446#issuecomment-122877546
214
+     * @link https://bugzilla.redhat.com/show_bug.cgi?id=1241172
215
+     * @return string
216
+     */
217
+    private function isUsedTlsLibOutdated() {
218
+        // Don't run check when:
219
+        // 1. Server has `has_internet_connection` set to false
220
+        // 2. AppStore AND S2S is disabled
221
+        if (!$this->config->getSystemValue('has_internet_connection', true)) {
222
+            return '';
223
+        }
224
+        if (!$this->config->getSystemValue('appstoreenabled', true)
225
+            && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'no'
226
+            && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'no') {
227
+            return '';
228
+        }
229
+
230
+        $versionString = $this->getCurlVersion();
231
+        if (isset($versionString['ssl_version'])) {
232
+            $versionString = $versionString['ssl_version'];
233
+        } else {
234
+            return '';
235
+        }
236
+
237
+        $features = (string)$this->l10n->t('installing and updating apps via the app store or Federated Cloud Sharing');
238
+        if (!$this->config->getSystemValue('appstoreenabled', true)) {
239
+            $features = (string)$this->l10n->t('Federated Cloud Sharing');
240
+        }
241
+
242
+        // Check if at least OpenSSL after 1.01d or 1.0.2b
243
+        if (strpos($versionString, 'OpenSSL/') === 0) {
244
+            $majorVersion = substr($versionString, 8, 5);
245
+            $patchRelease = substr($versionString, 13, 6);
246
+
247
+            if (($majorVersion === '1.0.1' && ord($patchRelease) < ord('d')) ||
248
+                ($majorVersion === '1.0.2' && ord($patchRelease) < ord('b'))) {
249
+                return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['OpenSSL', $versionString, $features]);
250
+            }
251
+        }
252
+
253
+        // Check if NSS and perform heuristic check
254
+        if (strpos($versionString, 'NSS/') === 0) {
255
+            try {
256
+                $firstClient = $this->clientService->newClient();
257
+                $firstClient->get('https://nextcloud.com/');
258
+
259
+                $secondClient = $this->clientService->newClient();
260
+                $secondClient->get('https://nextcloud.com/');
261
+            } catch (ClientException $e) {
262
+                if ($e->getResponse()->getStatusCode() === 400) {
263
+                    return $this->l10n->t('cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably.', ['NSS', $versionString, $features]);
264
+                }
265
+            } catch (\Exception $e) {
266
+                $this->logger->logException($e, ['app' => 'settings', 'level' => \OCP\ILogger::WARN]);
267
+                return $this->l10n->t('Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the nextcloud log file for more details.');
268
+            }
269
+        }
270
+
271
+        return '';
272
+    }
273
+
274
+    /**
275
+     * Whether the version is outdated
276
+     *
277
+     * @return bool
278
+     */
279
+    protected function isPhpOutdated(): bool {
280
+        return PHP_VERSION_ID < 70300;
281
+    }
282
+
283
+    /**
284
+     * Whether the php version is still supported (at time of release)
285
+     * according to: https://secure.php.net/supported-versions.php
286
+     *
287
+     * @return array
288
+     */
289
+    private function isPhpSupported(): array {
290
+        return ['eol' => $this->isPhpOutdated(), 'version' => PHP_VERSION];
291
+    }
292
+
293
+    /**
294
+     * Check if the reverse proxy configuration is working as expected
295
+     *
296
+     * @return bool
297
+     */
298
+    private function forwardedForHeadersWorking() {
299
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
300
+        $remoteAddress = $this->request->getHeader('REMOTE_ADDR');
301
+
302
+        if (empty($trustedProxies) && $this->request->getHeader('X-Forwarded-Host') !== '') {
303
+            return false;
304
+        }
305
+
306
+        if (\is_array($trustedProxies) && \in_array($remoteAddress, $trustedProxies, true)) {
307
+            return $remoteAddress !== $this->request->getRemoteAddress();
308
+        }
309
+
310
+        // either not enabled or working correctly
311
+        return true;
312
+    }
313
+
314
+    /**
315
+     * Checks if the correct memcache module for PHP is installed. Only
316
+     * fails if memcached is configured and the working module is not installed.
317
+     *
318
+     * @return bool
319
+     */
320
+    private function isCorrectMemcachedPHPModuleInstalled() {
321
+        if ($this->config->getSystemValue('memcache.distributed', null) !== '\OC\Memcache\Memcached') {
322
+            return true;
323
+        }
324
+
325
+        // there are two different memcached modules for PHP
326
+        // we only support memcached and not memcache
327
+        // https://code.google.com/p/memcached/wiki/PHPClientComparison
328
+        return !(!extension_loaded('memcached') && extension_loaded('memcache'));
329
+    }
330
+
331
+    /**
332
+     * Checks if set_time_limit is not disabled.
333
+     *
334
+     * @return bool
335
+     */
336
+    private function isSettimelimitAvailable() {
337
+        if (function_exists('set_time_limit')
338
+            && strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
339
+            return true;
340
+        }
341
+
342
+        return false;
343
+    }
344
+
345
+    /**
346
+     * @return RedirectResponse
347
+     */
348
+    public function rescanFailedIntegrityCheck() {
349
+        $this->checker->runInstanceVerification();
350
+        return new RedirectResponse(
351
+            $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview'])
352
+        );
353
+    }
354
+
355
+    /**
356
+     * @NoCSRFRequired
357
+     * @return DataResponse
358
+     */
359
+    public function getFailedIntegrityCheckFiles() {
360
+        if (!$this->checker->isCodeCheckEnforced()) {
361
+            return new DataDisplayResponse('Integrity checker has been disabled. Integrity cannot be verified.');
362
+        }
363
+
364
+        $completeResults = $this->checker->getResults();
365
+
366
+        if (!empty($completeResults)) {
367
+            $formattedTextResponse = 'Technical information
368 368
 =====================
369 369
 The following list covers which files have failed the integrity check. Please read
370 370
 the previous linked documentation to learn more about the errors and how to fix
@@ -373,387 +373,387 @@  discard block
 block discarded – undo
373 373
 Results
374 374
 =======
375 375
 ';
376
-			foreach ($completeResults as $context => $contextResult) {
377
-				$formattedTextResponse .= "- $context\n";
378
-
379
-				foreach ($contextResult as $category => $result) {
380
-					$formattedTextResponse .= "\t- $category\n";
381
-					if ($category !== 'EXCEPTION') {
382
-						foreach ($result as $key => $results) {
383
-							$formattedTextResponse .= "\t\t- $key\n";
384
-						}
385
-					} else {
386
-						foreach ($result as $key => $results) {
387
-							$formattedTextResponse .= "\t\t- $results\n";
388
-						}
389
-					}
390
-				}
391
-			}
392
-
393
-			$formattedTextResponse .= '
376
+            foreach ($completeResults as $context => $contextResult) {
377
+                $formattedTextResponse .= "- $context\n";
378
+
379
+                foreach ($contextResult as $category => $result) {
380
+                    $formattedTextResponse .= "\t- $category\n";
381
+                    if ($category !== 'EXCEPTION') {
382
+                        foreach ($result as $key => $results) {
383
+                            $formattedTextResponse .= "\t\t- $key\n";
384
+                        }
385
+                    } else {
386
+                        foreach ($result as $key => $results) {
387
+                            $formattedTextResponse .= "\t\t- $results\n";
388
+                        }
389
+                    }
390
+                }
391
+            }
392
+
393
+            $formattedTextResponse .= '
394 394
 Raw output
395 395
 ==========
396 396
 ';
397
-			$formattedTextResponse .= print_r($completeResults, true);
398
-		} else {
399
-			$formattedTextResponse = 'No errors have been found.';
400
-		}
401
-
402
-
403
-		$response = new DataDisplayResponse(
404
-			$formattedTextResponse,
405
-			Http::STATUS_OK,
406
-			[
407
-				'Content-Type' => 'text/plain',
408
-			]
409
-		);
410
-
411
-		return $response;
412
-	}
413
-
414
-	/**
415
-	 * Checks whether a PHP opcache is properly set up
416
-	 * @return bool
417
-	 */
418
-	protected function isOpcacheProperlySetup() {
419
-		if (!$this->iniGetWrapper->getBool('opcache.enable')) {
420
-			return false;
421
-		}
422
-
423
-		if (!$this->iniGetWrapper->getBool('opcache.save_comments')) {
424
-			return false;
425
-		}
426
-
427
-		if ($this->iniGetWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
428
-			return false;
429
-		}
430
-
431
-		if ($this->iniGetWrapper->getNumeric('opcache.memory_consumption') < 128) {
432
-			return false;
433
-		}
434
-
435
-		if ($this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
436
-			return false;
437
-		}
438
-
439
-		return true;
440
-	}
441
-
442
-	/**
443
-	 * Check if the required FreeType functions are present
444
-	 * @return bool
445
-	 */
446
-	protected function hasFreeTypeSupport() {
447
-		return function_exists('imagettfbbox') && function_exists('imagettftext');
448
-	}
449
-
450
-	protected function hasMissingIndexes(): array {
451
-		$indexInfo = new MissingIndexInformation();
452
-		// Dispatch event so apps can also hint for pending index updates if needed
453
-		$event = new GenericEvent($indexInfo);
454
-		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
455
-
456
-		return $indexInfo->getListOfMissingIndexes();
457
-	}
458
-
459
-	protected function hasMissingPrimaryKeys(): array {
460
-		$info = new MissingPrimaryKeyInformation();
461
-		// Dispatch event so apps can also hint for pending index updates if needed
462
-		$event = new GenericEvent($info);
463
-		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT, $event);
464
-
465
-		return $info->getListOfMissingPrimaryKeys();
466
-	}
467
-
468
-	protected function hasMissingColumns(): array {
469
-		$indexInfo = new MissingColumnInformation();
470
-		// Dispatch event so apps can also hint for pending index updates if needed
471
-		$event = new GenericEvent($indexInfo);
472
-		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
473
-
474
-		return $indexInfo->getListOfMissingColumns();
475
-	}
476
-
477
-	protected function isSqliteUsed() {
478
-		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
479
-	}
480
-
481
-	protected function isReadOnlyConfig(): bool {
482
-		return \OC_Helper::isReadOnlyConfigEnabled();
483
-	}
484
-
485
-	protected function hasValidTransactionIsolationLevel(): bool {
486
-		try {
487
-			if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
488
-				return true;
489
-			}
490
-
491
-			return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
492
-		} catch (DBALException $e) {
493
-			// ignore
494
-		}
495
-
496
-		return true;
497
-	}
498
-
499
-	protected function hasFileinfoInstalled(): bool {
500
-		return \OC_Util::fileInfoLoaded();
501
-	}
502
-
503
-	protected function hasWorkingFileLocking(): bool {
504
-		return !($this->lockingProvider instanceof NoopLockingProvider);
505
-	}
506
-
507
-	protected function getSuggestedOverwriteCliURL(): string {
508
-		$suggestedOverwriteCliUrl = '';
509
-		if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
510
-			$suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
511
-			if (!$this->config->getSystemValue('config_is_read_only', false)) {
512
-				// Set the overwrite URL when it was not set yet.
513
-				$this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
514
-				$suggestedOverwriteCliUrl = '';
515
-			}
516
-		}
517
-		return $suggestedOverwriteCliUrl;
518
-	}
519
-
520
-	protected function getLastCronInfo(): array {
521
-		$lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
522
-		return [
523
-			'diffInSeconds' => time() - $lastCronRun,
524
-			'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
525
-			'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
526
-		];
527
-	}
528
-
529
-	protected function getCronErrors() {
530
-		$errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
531
-
532
-		if (is_array($errors)) {
533
-			return $errors;
534
-		}
535
-
536
-		return [];
537
-	}
538
-
539
-	protected function hasOpcacheLoaded(): bool {
540
-		return extension_loaded('Zend OPcache');
541
-	}
542
-
543
-	/**
544
-	 * Iterates through the configured app roots and
545
-	 * tests if the subdirectories are owned by the same user than the current user.
546
-	 *
547
-	 * @return array
548
-	 */
549
-	protected function getAppDirsWithDifferentOwner(): array {
550
-		$currentUser = posix_getuid();
551
-		$appDirsWithDifferentOwner = [[]];
552
-
553
-		foreach (OC::$APPSROOTS as $appRoot) {
554
-			if ($appRoot['writable'] === true) {
555
-				$appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
556
-			}
557
-		}
558
-
559
-		$appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
560
-		sort($appDirsWithDifferentOwner);
561
-
562
-		return $appDirsWithDifferentOwner;
563
-	}
564
-
565
-	/**
566
-	 * Tests if the directories for one apps directory are writable by the current user.
567
-	 *
568
-	 * @param int $currentUser The current user
569
-	 * @param array $appRoot The app root config
570
-	 * @return string[] The none writable directory paths inside the app root
571
-	 */
572
-	private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
573
-		$appDirsWithDifferentOwner = [];
574
-		$appsPath = $appRoot['path'];
575
-		$appsDir = new DirectoryIterator($appRoot['path']);
576
-
577
-		foreach ($appsDir as $fileInfo) {
578
-			if ($fileInfo->isDir() && !$fileInfo->isDot()) {
579
-				$absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
580
-				$appDirUser = fileowner($absAppPath);
581
-				if ($appDirUser !== $currentUser) {
582
-					$appDirsWithDifferentOwner[] = $absAppPath;
583
-				}
584
-			}
585
-		}
586
-
587
-		return $appDirsWithDifferentOwner;
588
-	}
589
-
590
-	/**
591
-	 * Checks for potential PHP modules that would improve the instance
592
-	 *
593
-	 * @return string[] A list of PHP modules that is recommended
594
-	 */
595
-	protected function hasRecommendedPHPModules(): array {
596
-		$recommendedPHPModules = [];
597
-
598
-		if (!extension_loaded('intl')) {
599
-			$recommendedPHPModules[] = 'intl';
600
-		}
601
-
602
-		if (!extension_loaded('bcmath')) {
603
-			$recommendedPHPModules[] = 'bcmath';
604
-		}
605
-
606
-		if (!extension_loaded('gmp')) {
607
-			$recommendedPHPModules[] = 'gmp';
608
-		}
609
-
610
-		if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
611
-			if (!extension_loaded('imagick')) {
612
-				$recommendedPHPModules[] = 'imagick';
613
-			}
614
-		}
615
-
616
-		return $recommendedPHPModules;
617
-	}
618
-
619
-	protected function isMysqlUsedWithoutUTF8MB4(): bool {
620
-		return ($this->config->getSystemValue('dbtype', 'sqlite') === 'mysql') && ($this->config->getSystemValue('mysql.utf8mb4', false) === false);
621
-	}
622
-
623
-	protected function hasBigIntConversionPendingColumns(): array {
624
-		// copy of ConvertFilecacheBigInt::getColumnsByTable()
625
-		$tables = [
626
-			'activity' => ['activity_id', 'object_id'],
627
-			'activity_mq' => ['mail_id'],
628
-			'authtoken' => ['id'],
629
-			'bruteforce_attempts' => ['id'],
630
-			'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
631
-			'filecache_extended' => ['fileid'],
632
-			'file_locks' => ['id'],
633
-			'jobs' => ['id'],
634
-			'mimetypes' => ['id'],
635
-			'mounts' => ['id', 'storage_id', 'root_id', 'mount_id'],
636
-			'storages' => ['numeric_id'],
637
-		];
638
-
639
-		$schema = new SchemaWrapper($this->db);
640
-		$isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
641
-		$pendingColumns = [];
642
-
643
-		foreach ($tables as $tableName => $columns) {
644
-			if (!$schema->hasTable($tableName)) {
645
-				continue;
646
-			}
647
-
648
-			$table = $schema->getTable($tableName);
649
-			foreach ($columns as $columnName) {
650
-				$column = $table->getColumn($columnName);
651
-				$isAutoIncrement = $column->getAutoincrement();
652
-				$isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
653
-				if ($column->getType()->getName() !== Types::BIGINT && !$isAutoIncrementOnSqlite) {
654
-					$pendingColumns[] = $tableName . '.' . $columnName;
655
-				}
656
-			}
657
-		}
658
-
659
-		return $pendingColumns;
660
-	}
661
-
662
-	protected function isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(): bool {
663
-		$objectStore = $this->config->getSystemValue('objectstore', null);
664
-		$objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
665
-
666
-		if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
667
-			return true;
668
-		}
669
-
670
-		if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
671
-			return true;
672
-		}
673
-
674
-		if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
675
-			return true;
676
-		}
677
-
678
-		$tempPath = sys_get_temp_dir();
679
-		if (!is_dir($tempPath)) {
680
-			$this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
681
-			return false;
682
-		}
683
-		$freeSpaceInTemp = disk_free_space($tempPath);
684
-		if ($freeSpaceInTemp === false) {
685
-			$this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
686
-			return false;
687
-		}
688
-
689
-		$freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
690
-		if ($freeSpaceInTempInGB > 50) {
691
-			return true;
692
-		}
693
-
694
-		$this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
695
-		return false;
696
-	}
697
-
698
-	protected function imageMagickLacksSVGSupport(): bool {
699
-		return extension_loaded('imagick') && count(\Imagick::queryFormats('SVG')) === 0;
700
-	}
701
-
702
-	/**
703
-	 * @return DataResponse
704
-	 */
705
-	public function check() {
706
-		$phpDefaultCharset = new PhpDefaultCharset();
707
-		$phpOutputBuffering = new PhpOutputBuffering();
708
-		$legacySSEKeyFormat = new LegacySSEKeyFormat($this->l10n, $this->config, $this->urlGenerator);
709
-		$checkUserCertificates = new CheckUserCertificates($this->l10n, $this->config, $this->urlGenerator);
710
-
711
-		return new DataResponse(
712
-			[
713
-				'isGetenvServerWorking' => !empty(getenv('PATH')),
714
-				'isReadOnlyConfig' => $this->isReadOnlyConfig(),
715
-				'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
716
-				'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
717
-				'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
718
-				'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
719
-				'cronInfo' => $this->getLastCronInfo(),
720
-				'cronErrors' => $this->getCronErrors(),
721
-				'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(),
722
-				'isMemcacheConfigured' => $this->isMemcacheConfigured(),
723
-				'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
724
-				'isRandomnessSecure' => $this->isRandomnessSecure(),
725
-				'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
726
-				'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
727
-				'phpSupported' => $this->isPhpSupported(),
728
-				'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
729
-				'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
730
-				'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
731
-				'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
732
-				'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
733
-				'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
734
-				'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
735
-				'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
736
-				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
737
-				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
738
-				'missingPrimaryKeys' => $this->hasMissingPrimaryKeys(),
739
-				'missingIndexes' => $this->hasMissingIndexes(),
740
-				'missingColumns' => $this->hasMissingColumns(),
741
-				'isSqliteUsed' => $this->isSqliteUsed(),
742
-				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
743
-				'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
744
-				'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
745
-				'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
746
-				'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
747
-				'isMysqlUsedWithoutUTF8MB4' => $this->isMysqlUsedWithoutUTF8MB4(),
748
-				'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => $this->isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(),
749
-				'reverseProxyGeneratedURL' => $this->urlGenerator->getAbsoluteURL('index.php'),
750
-				'imageMagickLacksSVGSupport' => $this->imageMagickLacksSVGSupport(),
751
-				PhpDefaultCharset::class => ['pass' => $phpDefaultCharset->run(), 'description' => $phpDefaultCharset->description(), 'severity' => $phpDefaultCharset->severity()],
752
-				PhpOutputBuffering::class => ['pass' => $phpOutputBuffering->run(), 'description' => $phpOutputBuffering->description(), 'severity' => $phpOutputBuffering->severity()],
753
-				LegacySSEKeyFormat::class => ['pass' => $legacySSEKeyFormat->run(), 'description' => $legacySSEKeyFormat->description(), 'severity' => $legacySSEKeyFormat->severity(), 'linkToDocumentation' => $legacySSEKeyFormat->linkToDocumentation()],
754
-				CheckUserCertificates::class => ['pass' => $checkUserCertificates->run(), 'description' => $checkUserCertificates->description(), 'severity' => $checkUserCertificates->severity(), 'elements' => $checkUserCertificates->elements()],
755
-				'isDefaultPhoneRegionSet' => $this->config->getSystemValueString('default_phone_region', '') !== '',
756
-			]
757
-		);
758
-	}
397
+            $formattedTextResponse .= print_r($completeResults, true);
398
+        } else {
399
+            $formattedTextResponse = 'No errors have been found.';
400
+        }
401
+
402
+
403
+        $response = new DataDisplayResponse(
404
+            $formattedTextResponse,
405
+            Http::STATUS_OK,
406
+            [
407
+                'Content-Type' => 'text/plain',
408
+            ]
409
+        );
410
+
411
+        return $response;
412
+    }
413
+
414
+    /**
415
+     * Checks whether a PHP opcache is properly set up
416
+     * @return bool
417
+     */
418
+    protected function isOpcacheProperlySetup() {
419
+        if (!$this->iniGetWrapper->getBool('opcache.enable')) {
420
+            return false;
421
+        }
422
+
423
+        if (!$this->iniGetWrapper->getBool('opcache.save_comments')) {
424
+            return false;
425
+        }
426
+
427
+        if ($this->iniGetWrapper->getNumeric('opcache.max_accelerated_files') < 10000) {
428
+            return false;
429
+        }
430
+
431
+        if ($this->iniGetWrapper->getNumeric('opcache.memory_consumption') < 128) {
432
+            return false;
433
+        }
434
+
435
+        if ($this->iniGetWrapper->getNumeric('opcache.interned_strings_buffer') < 8) {
436
+            return false;
437
+        }
438
+
439
+        return true;
440
+    }
441
+
442
+    /**
443
+     * Check if the required FreeType functions are present
444
+     * @return bool
445
+     */
446
+    protected function hasFreeTypeSupport() {
447
+        return function_exists('imagettfbbox') && function_exists('imagettftext');
448
+    }
449
+
450
+    protected function hasMissingIndexes(): array {
451
+        $indexInfo = new MissingIndexInformation();
452
+        // Dispatch event so apps can also hint for pending index updates if needed
453
+        $event = new GenericEvent($indexInfo);
454
+        $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
455
+
456
+        return $indexInfo->getListOfMissingIndexes();
457
+    }
458
+
459
+    protected function hasMissingPrimaryKeys(): array {
460
+        $info = new MissingPrimaryKeyInformation();
461
+        // Dispatch event so apps can also hint for pending index updates if needed
462
+        $event = new GenericEvent($info);
463
+        $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT, $event);
464
+
465
+        return $info->getListOfMissingPrimaryKeys();
466
+    }
467
+
468
+    protected function hasMissingColumns(): array {
469
+        $indexInfo = new MissingColumnInformation();
470
+        // Dispatch event so apps can also hint for pending index updates if needed
471
+        $event = new GenericEvent($indexInfo);
472
+        $this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
473
+
474
+        return $indexInfo->getListOfMissingColumns();
475
+    }
476
+
477
+    protected function isSqliteUsed() {
478
+        return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
479
+    }
480
+
481
+    protected function isReadOnlyConfig(): bool {
482
+        return \OC_Helper::isReadOnlyConfigEnabled();
483
+    }
484
+
485
+    protected function hasValidTransactionIsolationLevel(): bool {
486
+        try {
487
+            if ($this->db->getDatabasePlatform() instanceof SqlitePlatform) {
488
+                return true;
489
+            }
490
+
491
+            return $this->db->getTransactionIsolation() === Connection::TRANSACTION_READ_COMMITTED;
492
+        } catch (DBALException $e) {
493
+            // ignore
494
+        }
495
+
496
+        return true;
497
+    }
498
+
499
+    protected function hasFileinfoInstalled(): bool {
500
+        return \OC_Util::fileInfoLoaded();
501
+    }
502
+
503
+    protected function hasWorkingFileLocking(): bool {
504
+        return !($this->lockingProvider instanceof NoopLockingProvider);
505
+    }
506
+
507
+    protected function getSuggestedOverwriteCliURL(): string {
508
+        $suggestedOverwriteCliUrl = '';
509
+        if ($this->config->getSystemValue('overwrite.cli.url', '') === '') {
510
+            $suggestedOverwriteCliUrl = $this->request->getServerProtocol() . '://' . $this->request->getInsecureServerHost() . \OC::$WEBROOT;
511
+            if (!$this->config->getSystemValue('config_is_read_only', false)) {
512
+                // Set the overwrite URL when it was not set yet.
513
+                $this->config->setSystemValue('overwrite.cli.url', $suggestedOverwriteCliUrl);
514
+                $suggestedOverwriteCliUrl = '';
515
+            }
516
+        }
517
+        return $suggestedOverwriteCliUrl;
518
+    }
519
+
520
+    protected function getLastCronInfo(): array {
521
+        $lastCronRun = $this->config->getAppValue('core', 'lastcron', 0);
522
+        return [
523
+            'diffInSeconds' => time() - $lastCronRun,
524
+            'relativeTime' => $this->dateTimeFormatter->formatTimeSpan($lastCronRun),
525
+            'backgroundJobsUrl' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'server']) . '#backgroundjobs',
526
+        ];
527
+    }
528
+
529
+    protected function getCronErrors() {
530
+        $errors = json_decode($this->config->getAppValue('core', 'cronErrors', ''), true);
531
+
532
+        if (is_array($errors)) {
533
+            return $errors;
534
+        }
535
+
536
+        return [];
537
+    }
538
+
539
+    protected function hasOpcacheLoaded(): bool {
540
+        return extension_loaded('Zend OPcache');
541
+    }
542
+
543
+    /**
544
+     * Iterates through the configured app roots and
545
+     * tests if the subdirectories are owned by the same user than the current user.
546
+     *
547
+     * @return array
548
+     */
549
+    protected function getAppDirsWithDifferentOwner(): array {
550
+        $currentUser = posix_getuid();
551
+        $appDirsWithDifferentOwner = [[]];
552
+
553
+        foreach (OC::$APPSROOTS as $appRoot) {
554
+            if ($appRoot['writable'] === true) {
555
+                $appDirsWithDifferentOwner[] = $this->getAppDirsWithDifferentOwnerForAppRoot($currentUser, $appRoot);
556
+            }
557
+        }
558
+
559
+        $appDirsWithDifferentOwner = array_merge(...$appDirsWithDifferentOwner);
560
+        sort($appDirsWithDifferentOwner);
561
+
562
+        return $appDirsWithDifferentOwner;
563
+    }
564
+
565
+    /**
566
+     * Tests if the directories for one apps directory are writable by the current user.
567
+     *
568
+     * @param int $currentUser The current user
569
+     * @param array $appRoot The app root config
570
+     * @return string[] The none writable directory paths inside the app root
571
+     */
572
+    private function getAppDirsWithDifferentOwnerForAppRoot(int $currentUser, array $appRoot): array {
573
+        $appDirsWithDifferentOwner = [];
574
+        $appsPath = $appRoot['path'];
575
+        $appsDir = new DirectoryIterator($appRoot['path']);
576
+
577
+        foreach ($appsDir as $fileInfo) {
578
+            if ($fileInfo->isDir() && !$fileInfo->isDot()) {
579
+                $absAppPath = $appsPath . DIRECTORY_SEPARATOR . $fileInfo->getFilename();
580
+                $appDirUser = fileowner($absAppPath);
581
+                if ($appDirUser !== $currentUser) {
582
+                    $appDirsWithDifferentOwner[] = $absAppPath;
583
+                }
584
+            }
585
+        }
586
+
587
+        return $appDirsWithDifferentOwner;
588
+    }
589
+
590
+    /**
591
+     * Checks for potential PHP modules that would improve the instance
592
+     *
593
+     * @return string[] A list of PHP modules that is recommended
594
+     */
595
+    protected function hasRecommendedPHPModules(): array {
596
+        $recommendedPHPModules = [];
597
+
598
+        if (!extension_loaded('intl')) {
599
+            $recommendedPHPModules[] = 'intl';
600
+        }
601
+
602
+        if (!extension_loaded('bcmath')) {
603
+            $recommendedPHPModules[] = 'bcmath';
604
+        }
605
+
606
+        if (!extension_loaded('gmp')) {
607
+            $recommendedPHPModules[] = 'gmp';
608
+        }
609
+
610
+        if ($this->config->getAppValue('theming', 'enabled', 'no') === 'yes') {
611
+            if (!extension_loaded('imagick')) {
612
+                $recommendedPHPModules[] = 'imagick';
613
+            }
614
+        }
615
+
616
+        return $recommendedPHPModules;
617
+    }
618
+
619
+    protected function isMysqlUsedWithoutUTF8MB4(): bool {
620
+        return ($this->config->getSystemValue('dbtype', 'sqlite') === 'mysql') && ($this->config->getSystemValue('mysql.utf8mb4', false) === false);
621
+    }
622
+
623
+    protected function hasBigIntConversionPendingColumns(): array {
624
+        // copy of ConvertFilecacheBigInt::getColumnsByTable()
625
+        $tables = [
626
+            'activity' => ['activity_id', 'object_id'],
627
+            'activity_mq' => ['mail_id'],
628
+            'authtoken' => ['id'],
629
+            'bruteforce_attempts' => ['id'],
630
+            'filecache' => ['fileid', 'storage', 'parent', 'mimetype', 'mimepart', 'mtime', 'storage_mtime'],
631
+            'filecache_extended' => ['fileid'],
632
+            'file_locks' => ['id'],
633
+            'jobs' => ['id'],
634
+            'mimetypes' => ['id'],
635
+            'mounts' => ['id', 'storage_id', 'root_id', 'mount_id'],
636
+            'storages' => ['numeric_id'],
637
+        ];
638
+
639
+        $schema = new SchemaWrapper($this->db);
640
+        $isSqlite = $this->db->getDatabasePlatform() instanceof SqlitePlatform;
641
+        $pendingColumns = [];
642
+
643
+        foreach ($tables as $tableName => $columns) {
644
+            if (!$schema->hasTable($tableName)) {
645
+                continue;
646
+            }
647
+
648
+            $table = $schema->getTable($tableName);
649
+            foreach ($columns as $columnName) {
650
+                $column = $table->getColumn($columnName);
651
+                $isAutoIncrement = $column->getAutoincrement();
652
+                $isAutoIncrementOnSqlite = $isSqlite && $isAutoIncrement;
653
+                if ($column->getType()->getName() !== Types::BIGINT && !$isAutoIncrementOnSqlite) {
654
+                    $pendingColumns[] = $tableName . '.' . $columnName;
655
+                }
656
+            }
657
+        }
658
+
659
+        return $pendingColumns;
660
+    }
661
+
662
+    protected function isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(): bool {
663
+        $objectStore = $this->config->getSystemValue('objectstore', null);
664
+        $objectStoreMultibucket = $this->config->getSystemValue('objectstore_multibucket', null);
665
+
666
+        if (!isset($objectStoreMultibucket) && !isset($objectStore)) {
667
+            return true;
668
+        }
669
+
670
+        if (isset($objectStoreMultibucket['class']) && $objectStoreMultibucket['class'] !== 'OC\\Files\\ObjectStore\\S3') {
671
+            return true;
672
+        }
673
+
674
+        if (isset($objectStore['class']) && $objectStore['class'] !== 'OC\\Files\\ObjectStore\\S3') {
675
+            return true;
676
+        }
677
+
678
+        $tempPath = sys_get_temp_dir();
679
+        if (!is_dir($tempPath)) {
680
+            $this->logger->error('Error while checking the temporary PHP path - it was not properly set to a directory. value: ' . $tempPath);
681
+            return false;
682
+        }
683
+        $freeSpaceInTemp = disk_free_space($tempPath);
684
+        if ($freeSpaceInTemp === false) {
685
+            $this->logger->error('Error while checking the available disk space of temporary PHP path - no free disk space returned. temporary path: ' . $tempPath);
686
+            return false;
687
+        }
688
+
689
+        $freeSpaceInTempInGB = $freeSpaceInTemp / 1024 / 1024 / 1024;
690
+        if ($freeSpaceInTempInGB > 50) {
691
+            return true;
692
+        }
693
+
694
+        $this->logger->warning('Checking the available space in the temporary path resulted in ' . round($freeSpaceInTempInGB, 1) . ' GB instead of the recommended 50GB. Path: ' . $tempPath);
695
+        return false;
696
+    }
697
+
698
+    protected function imageMagickLacksSVGSupport(): bool {
699
+        return extension_loaded('imagick') && count(\Imagick::queryFormats('SVG')) === 0;
700
+    }
701
+
702
+    /**
703
+     * @return DataResponse
704
+     */
705
+    public function check() {
706
+        $phpDefaultCharset = new PhpDefaultCharset();
707
+        $phpOutputBuffering = new PhpOutputBuffering();
708
+        $legacySSEKeyFormat = new LegacySSEKeyFormat($this->l10n, $this->config, $this->urlGenerator);
709
+        $checkUserCertificates = new CheckUserCertificates($this->l10n, $this->config, $this->urlGenerator);
710
+
711
+        return new DataResponse(
712
+            [
713
+                'isGetenvServerWorking' => !empty(getenv('PATH')),
714
+                'isReadOnlyConfig' => $this->isReadOnlyConfig(),
715
+                'hasValidTransactionIsolationLevel' => $this->hasValidTransactionIsolationLevel(),
716
+                'hasFileinfoInstalled' => $this->hasFileinfoInstalled(),
717
+                'hasWorkingFileLocking' => $this->hasWorkingFileLocking(),
718
+                'suggestedOverwriteCliURL' => $this->getSuggestedOverwriteCliURL(),
719
+                'cronInfo' => $this->getLastCronInfo(),
720
+                'cronErrors' => $this->getCronErrors(),
721
+                'serverHasInternetConnectionProblems' => $this->hasInternetConnectivityProblems(),
722
+                'isMemcacheConfigured' => $this->isMemcacheConfigured(),
723
+                'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
724
+                'isRandomnessSecure' => $this->isRandomnessSecure(),
725
+                'securityDocs' => $this->urlGenerator->linkToDocs('admin-security'),
726
+                'isUsedTlsLibOutdated' => $this->isUsedTlsLibOutdated(),
727
+                'phpSupported' => $this->isPhpSupported(),
728
+                'forwardedForHeadersWorking' => $this->forwardedForHeadersWorking(),
729
+                'reverseProxyDocs' => $this->urlGenerator->linkToDocs('admin-reverse-proxy'),
730
+                'isCorrectMemcachedPHPModuleInstalled' => $this->isCorrectMemcachedPHPModuleInstalled(),
731
+                'hasPassedCodeIntegrityCheck' => $this->checker->hasPassedCheck(),
732
+                'codeIntegrityCheckerDocumentation' => $this->urlGenerator->linkToDocs('admin-code-integrity'),
733
+                'isOpcacheProperlySetup' => $this->isOpcacheProperlySetup(),
734
+                'hasOpcacheLoaded' => $this->hasOpcacheLoaded(),
735
+                'phpOpcacheDocumentation' => $this->urlGenerator->linkToDocs('admin-php-opcache'),
736
+                'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
737
+                'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
738
+                'missingPrimaryKeys' => $this->hasMissingPrimaryKeys(),
739
+                'missingIndexes' => $this->hasMissingIndexes(),
740
+                'missingColumns' => $this->hasMissingColumns(),
741
+                'isSqliteUsed' => $this->isSqliteUsed(),
742
+                'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
743
+                'isMemoryLimitSufficient' => $this->memoryInfo->isMemoryLimitSufficient(),
744
+                'appDirsWithDifferentOwner' => $this->getAppDirsWithDifferentOwner(),
745
+                'recommendedPHPModules' => $this->hasRecommendedPHPModules(),
746
+                'pendingBigIntConversionColumns' => $this->hasBigIntConversionPendingColumns(),
747
+                'isMysqlUsedWithoutUTF8MB4' => $this->isMysqlUsedWithoutUTF8MB4(),
748
+                'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => $this->isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(),
749
+                'reverseProxyGeneratedURL' => $this->urlGenerator->getAbsoluteURL('index.php'),
750
+                'imageMagickLacksSVGSupport' => $this->imageMagickLacksSVGSupport(),
751
+                PhpDefaultCharset::class => ['pass' => $phpDefaultCharset->run(), 'description' => $phpDefaultCharset->description(), 'severity' => $phpDefaultCharset->severity()],
752
+                PhpOutputBuffering::class => ['pass' => $phpOutputBuffering->run(), 'description' => $phpOutputBuffering->description(), 'severity' => $phpOutputBuffering->severity()],
753
+                LegacySSEKeyFormat::class => ['pass' => $legacySSEKeyFormat->run(), 'description' => $legacySSEKeyFormat->description(), 'severity' => $legacySSEKeyFormat->severity(), 'linkToDocumentation' => $legacySSEKeyFormat->linkToDocumentation()],
754
+                CheckUserCertificates::class => ['pass' => $checkUserCertificates->run(), 'description' => $checkUserCertificates->description(), 'severity' => $checkUserCertificates->severity(), 'elements' => $checkUserCertificates->elements()],
755
+                'isDefaultPhoneRegionSet' => $this->config->getSystemValueString('default_phone_region', '') !== '',
756
+            ]
757
+        );
758
+    }
759 759
 }
Please login to merge, or discard this patch.