Passed
Push — master ( 1f2a9d...b6d734 )
by Christoph
16:55 queued 12s
created
apps/dav/lib/CardDAV/SystemAddressbook.php 1 patch
Indentation   +301 added lines, -301 removed lines patch added patch discarded remove patch
@@ -49,328 +49,328 @@
 block discarded – undo
49 49
 use function in_array;
50 50
 
51 51
 class SystemAddressbook extends AddressBook {
52
-	public const URI_SHARED = 'z-server-generated--system';
53
-	/** @var IConfig */
54
-	private $config;
55
-	private IUserSession $userSession;
56
-	private ?TrustedServers $trustedServers;
57
-	private ?IRequest $request;
58
-	private ?IGroupManager $groupManager;
52
+    public const URI_SHARED = 'z-server-generated--system';
53
+    /** @var IConfig */
54
+    private $config;
55
+    private IUserSession $userSession;
56
+    private ?TrustedServers $trustedServers;
57
+    private ?IRequest $request;
58
+    private ?IGroupManager $groupManager;
59 59
 
60
-	public function __construct(BackendInterface $carddavBackend,
61
-		array $addressBookInfo,
62
-		IL10N $l10n,
63
-		IConfig $config,
64
-		IUserSession $userSession,
65
-		?IRequest $request = null,
66
-		?TrustedServers $trustedServers = null,
67
-		?IGroupManager $groupManager) {
68
-		parent::__construct($carddavBackend, $addressBookInfo, $l10n);
69
-		$this->config = $config;
70
-		$this->userSession = $userSession;
71
-		$this->request = $request;
72
-		$this->trustedServers = $trustedServers;
73
-		$this->groupManager = $groupManager;
60
+    public function __construct(BackendInterface $carddavBackend,
61
+        array $addressBookInfo,
62
+        IL10N $l10n,
63
+        IConfig $config,
64
+        IUserSession $userSession,
65
+        ?IRequest $request = null,
66
+        ?TrustedServers $trustedServers = null,
67
+        ?IGroupManager $groupManager) {
68
+        parent::__construct($carddavBackend, $addressBookInfo, $l10n);
69
+        $this->config = $config;
70
+        $this->userSession = $userSession;
71
+        $this->request = $request;
72
+        $this->trustedServers = $trustedServers;
73
+        $this->groupManager = $groupManager;
74 74
 
75
-		$this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Accounts');
76
-		$this->addressBookInfo['{' . Plugin::NS_CARDDAV . '}addressbook-description'] = $l10n->t('System address book which holds all accounts');
77
-	}
75
+        $this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Accounts');
76
+        $this->addressBookInfo['{' . Plugin::NS_CARDDAV . '}addressbook-description'] = $l10n->t('System address book which holds all accounts');
77
+    }
78 78
 
79
-	/**
80
-	 * No checkbox checked -> Show only the same user
81
-	 * 'Allow username autocompletion in share dialog' -> show everyone
82
-	 * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' -> show only users in intersecting groups
83
-	 * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users based on phone number integration' -> show only the same user
84
-	 * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' + 'Allow username autocompletion to users based on phone number integration' -> show only users in intersecting groups
85
-	 */
86
-	public function getChildren() {
87
-		$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
88
-		$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
89
-		$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
90
-		$user = $this->userSession->getUser();
91
-		if (!$user) {
92
-			// Should never happen because we don't allow anonymous access
93
-			return [];
94
-		}
95
-		if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
96
-			$name = SyncService::getCardUri($user);
97
-			try {
98
-				return [parent::getChild($name)];
99
-			} catch (NotFound $e) {
100
-				return [];
101
-			}
102
-		}
103
-		if ($shareEnumerationGroup) {
104
-			if ($this->groupManager === null) {
105
-				// Group manager is not available, so we can't determine which data is safe
106
-				return [];
107
-			}
108
-			$groups = $this->groupManager->getUserGroups($user);
109
-			$names = [];
110
-			foreach ($groups as $group) {
111
-				$users = $group->getUsers();
112
-				foreach ($users as $groupUser) {
113
-					if ($groupUser->getBackendClassName() === 'Guests') {
114
-						continue;
115
-					}
116
-					$names[] = SyncService::getCardUri($groupUser);
117
-				}
118
-			}
119
-			return parent::getMultipleChildren(array_unique($names));
120
-		}
79
+    /**
80
+     * No checkbox checked -> Show only the same user
81
+     * 'Allow username autocompletion in share dialog' -> show everyone
82
+     * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' -> show only users in intersecting groups
83
+     * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users based on phone number integration' -> show only the same user
84
+     * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' + 'Allow username autocompletion to users based on phone number integration' -> show only users in intersecting groups
85
+     */
86
+    public function getChildren() {
87
+        $shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
88
+        $shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
89
+        $shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
90
+        $user = $this->userSession->getUser();
91
+        if (!$user) {
92
+            // Should never happen because we don't allow anonymous access
93
+            return [];
94
+        }
95
+        if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
96
+            $name = SyncService::getCardUri($user);
97
+            try {
98
+                return [parent::getChild($name)];
99
+            } catch (NotFound $e) {
100
+                return [];
101
+            }
102
+        }
103
+        if ($shareEnumerationGroup) {
104
+            if ($this->groupManager === null) {
105
+                // Group manager is not available, so we can't determine which data is safe
106
+                return [];
107
+            }
108
+            $groups = $this->groupManager->getUserGroups($user);
109
+            $names = [];
110
+            foreach ($groups as $group) {
111
+                $users = $group->getUsers();
112
+                foreach ($users as $groupUser) {
113
+                    if ($groupUser->getBackendClassName() === 'Guests') {
114
+                        continue;
115
+                    }
116
+                    $names[] = SyncService::getCardUri($groupUser);
117
+                }
118
+            }
119
+            return parent::getMultipleChildren(array_unique($names));
120
+        }
121 121
 
122
-		$children = parent::getChildren();
123
-		return array_filter($children, function (Card $child) {
124
-			// check only for URIs that begin with Guests:
125
-			return strpos($child->getName(), 'Guests:') !== 0;
126
-		});
127
-	}
122
+        $children = parent::getChildren();
123
+        return array_filter($children, function (Card $child) {
124
+            // check only for URIs that begin with Guests:
125
+            return strpos($child->getName(), 'Guests:') !== 0;
126
+        });
127
+    }
128 128
 
129
-	/**
130
-	 * @param array $paths
131
-	 * @return Card[]
132
-	 * @throws NotFound
133
-	 */
134
-	public function getMultipleChildren($paths): array {
135
-		$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
136
-		$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
137
-		$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
138
-		if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
139
-			$user = $this->userSession->getUser();
140
-			// No user or cards with no access
141
-			if ($user === null || !in_array(SyncService::getCardUri($user), $paths, true)) {
142
-				return [];
143
-			}
144
-			// Only return the own card
145
-			try {
146
-				return [parent::getChild(SyncService::getCardUri($user))];
147
-			} catch (NotFound $e) {
148
-				return [];
149
-			}
150
-		}
151
-		if ($shareEnumerationGroup) {
152
-			$user = $this->userSession->getUser();
153
-			if ($this->groupManager === null || $user === null) {
154
-				// Group manager or user is not available, so we can't determine which data is safe
155
-				return [];
156
-			}
157
-			$groups = $this->groupManager->getUserGroups($user);
158
-			$allowedNames = [];
159
-			foreach ($groups as $group) {
160
-				$users = $group->getUsers();
161
-				foreach ($users as $groupUser) {
162
-					if ($groupUser->getBackendClassName() === 'Guests') {
163
-						continue;
164
-					}
165
-					$allowedNames[] = SyncService::getCardUri($groupUser);
166
-				}
167
-			}
168
-			return parent::getMultipleChildren(array_intersect($paths, $allowedNames));
169
-		}
170
-		if (!$this->isFederation()) {
171
-			return parent::getMultipleChildren($paths);
172
-		}
129
+    /**
130
+     * @param array $paths
131
+     * @return Card[]
132
+     * @throws NotFound
133
+     */
134
+    public function getMultipleChildren($paths): array {
135
+        $shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
136
+        $shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
137
+        $shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
138
+        if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
139
+            $user = $this->userSession->getUser();
140
+            // No user or cards with no access
141
+            if ($user === null || !in_array(SyncService::getCardUri($user), $paths, true)) {
142
+                return [];
143
+            }
144
+            // Only return the own card
145
+            try {
146
+                return [parent::getChild(SyncService::getCardUri($user))];
147
+            } catch (NotFound $e) {
148
+                return [];
149
+            }
150
+        }
151
+        if ($shareEnumerationGroup) {
152
+            $user = $this->userSession->getUser();
153
+            if ($this->groupManager === null || $user === null) {
154
+                // Group manager or user is not available, so we can't determine which data is safe
155
+                return [];
156
+            }
157
+            $groups = $this->groupManager->getUserGroups($user);
158
+            $allowedNames = [];
159
+            foreach ($groups as $group) {
160
+                $users = $group->getUsers();
161
+                foreach ($users as $groupUser) {
162
+                    if ($groupUser->getBackendClassName() === 'Guests') {
163
+                        continue;
164
+                    }
165
+                    $allowedNames[] = SyncService::getCardUri($groupUser);
166
+                }
167
+            }
168
+            return parent::getMultipleChildren(array_intersect($paths, $allowedNames));
169
+        }
170
+        if (!$this->isFederation()) {
171
+            return parent::getMultipleChildren($paths);
172
+        }
173 173
 
174
-		$objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths);
175
-		$children = [];
176
-		/** @var array $obj */
177
-		foreach ($objs as $obj) {
178
-			if (empty($obj)) {
179
-				continue;
180
-			}
181
-			$carddata = $this->extractCarddata($obj);
182
-			if (empty($carddata)) {
183
-				continue;
184
-			} else {
185
-				$obj['carddata'] = $carddata;
186
-			}
187
-			$children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj);
188
-		}
189
-		return $children;
190
-	}
174
+        $objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths);
175
+        $children = [];
176
+        /** @var array $obj */
177
+        foreach ($objs as $obj) {
178
+            if (empty($obj)) {
179
+                continue;
180
+            }
181
+            $carddata = $this->extractCarddata($obj);
182
+            if (empty($carddata)) {
183
+                continue;
184
+            } else {
185
+                $obj['carddata'] = $carddata;
186
+            }
187
+            $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj);
188
+        }
189
+        return $children;
190
+    }
191 191
 
192
-	/**
193
-	 * @param string $name
194
-	 * @return Card
195
-	 * @throws NotFound
196
-	 * @throws Forbidden
197
-	 */
198
-	public function getChild($name): Card {
199
-		$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
200
-		$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
201
-		$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
202
-		if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
203
-			$currentUser = $this->userSession->getUser();
204
-			$ownName = $currentUser !== null ? SyncService::getCardUri($currentUser) : null;
205
-			if ($ownName === $name) {
206
-				return parent::getChild($name);
207
-			}
208
-			throw new Forbidden();
209
-		}
210
-		if ($shareEnumerationGroup) {
211
-			$user = $this->userSession->getUser();
212
-			if ($user === null || $this->groupManager === null) {
213
-				// Group manager is not available, so we can't determine which data is safe
214
-				throw new Forbidden();
215
-			}
216
-			$groups = $this->groupManager->getUserGroups($user);
217
-			foreach ($groups as $group) {
218
-				foreach ($group->getUsers() as $groupUser) {
219
-					if ($groupUser->getBackendClassName() === 'Guests') {
220
-						continue;
221
-					}
222
-					$otherName = SyncService::getCardUri($groupUser);
223
-					if ($otherName === $name) {
224
-						return parent::getChild($name);
225
-					}
226
-				}
227
-			}
228
-			throw new Forbidden();
229
-		}
230
-		if (!$this->isFederation()) {
231
-			return parent::getChild($name);
232
-		}
192
+    /**
193
+     * @param string $name
194
+     * @return Card
195
+     * @throws NotFound
196
+     * @throws Forbidden
197
+     */
198
+    public function getChild($name): Card {
199
+        $shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
200
+        $shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
201
+        $shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
202
+        if (!$shareEnumeration || (!$shareEnumerationGroup && $shareEnumerationPhone)) {
203
+            $currentUser = $this->userSession->getUser();
204
+            $ownName = $currentUser !== null ? SyncService::getCardUri($currentUser) : null;
205
+            if ($ownName === $name) {
206
+                return parent::getChild($name);
207
+            }
208
+            throw new Forbidden();
209
+        }
210
+        if ($shareEnumerationGroup) {
211
+            $user = $this->userSession->getUser();
212
+            if ($user === null || $this->groupManager === null) {
213
+                // Group manager is not available, so we can't determine which data is safe
214
+                throw new Forbidden();
215
+            }
216
+            $groups = $this->groupManager->getUserGroups($user);
217
+            foreach ($groups as $group) {
218
+                foreach ($group->getUsers() as $groupUser) {
219
+                    if ($groupUser->getBackendClassName() === 'Guests') {
220
+                        continue;
221
+                    }
222
+                    $otherName = SyncService::getCardUri($groupUser);
223
+                    if ($otherName === $name) {
224
+                        return parent::getChild($name);
225
+                    }
226
+                }
227
+            }
228
+            throw new Forbidden();
229
+        }
230
+        if (!$this->isFederation()) {
231
+            return parent::getChild($name);
232
+        }
233 233
 
234
-		$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
235
-		if (!$obj) {
236
-			throw new NotFound('Card not found');
237
-		}
238
-		$carddata = $this->extractCarddata($obj);
239
-		if (empty($carddata)) {
240
-			throw new Forbidden();
241
-		} else {
242
-			$obj['carddata'] = $carddata;
243
-		}
244
-		return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
245
-	}
234
+        $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
235
+        if (!$obj) {
236
+            throw new NotFound('Card not found');
237
+        }
238
+        $carddata = $this->extractCarddata($obj);
239
+        if (empty($carddata)) {
240
+            throw new Forbidden();
241
+        } else {
242
+            $obj['carddata'] = $carddata;
243
+        }
244
+        return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
245
+    }
246 246
 
247
-	/**
248
-	 * @throws UnsupportedLimitOnInitialSyncException
249
-	 */
250
-	public function getChanges($syncToken, $syncLevel, $limit = null) {
251
-		if (!$syncToken && $limit) {
252
-			throw new UnsupportedLimitOnInitialSyncException();
253
-		}
247
+    /**
248
+     * @throws UnsupportedLimitOnInitialSyncException
249
+     */
250
+    public function getChanges($syncToken, $syncLevel, $limit = null) {
251
+        if (!$syncToken && $limit) {
252
+            throw new UnsupportedLimitOnInitialSyncException();
253
+        }
254 254
 
255
-		if (!$this->carddavBackend instanceof SyncSupport) {
256
-			return null;
257
-		}
255
+        if (!$this->carddavBackend instanceof SyncSupport) {
256
+            return null;
257
+        }
258 258
 
259
-		if (!$this->isFederation()) {
260
-			return parent::getChanges($syncToken, $syncLevel, $limit);
261
-		}
259
+        if (!$this->isFederation()) {
260
+            return parent::getChanges($syncToken, $syncLevel, $limit);
261
+        }
262 262
 
263
-		$changed = $this->carddavBackend->getChangesForAddressBook(
264
-			$this->addressBookInfo['id'],
265
-			$syncToken,
266
-			$syncLevel,
267
-			$limit
268
-		);
263
+        $changed = $this->carddavBackend->getChangesForAddressBook(
264
+            $this->addressBookInfo['id'],
265
+            $syncToken,
266
+            $syncLevel,
267
+            $limit
268
+        );
269 269
 
270
-		if (empty($changed)) {
271
-			return $changed;
272
-		}
270
+        if (empty($changed)) {
271
+            return $changed;
272
+        }
273 273
 
274
-		$added = $modified = $deleted = [];
275
-		foreach ($changed['added'] as $uri) {
276
-			try {
277
-				$this->getChild($uri);
278
-				$added[] = $uri;
279
-			} catch (NotFound | Forbidden $e) {
280
-				$deleted[] = $uri;
281
-			}
282
-		}
283
-		foreach ($changed['modified'] as $uri) {
284
-			try {
285
-				$this->getChild($uri);
286
-				$modified[] = $uri;
287
-			} catch (NotFound | Forbidden $e) {
288
-				$deleted[] = $uri;
289
-			}
290
-		}
291
-		$changed['added'] = $added;
292
-		$changed['modified'] = $modified;
293
-		$changed['deleted'] = $deleted;
294
-		return $changed;
295
-	}
274
+        $added = $modified = $deleted = [];
275
+        foreach ($changed['added'] as $uri) {
276
+            try {
277
+                $this->getChild($uri);
278
+                $added[] = $uri;
279
+            } catch (NotFound | Forbidden $e) {
280
+                $deleted[] = $uri;
281
+            }
282
+        }
283
+        foreach ($changed['modified'] as $uri) {
284
+            try {
285
+                $this->getChild($uri);
286
+                $modified[] = $uri;
287
+            } catch (NotFound | Forbidden $e) {
288
+                $deleted[] = $uri;
289
+            }
290
+        }
291
+        $changed['added'] = $added;
292
+        $changed['modified'] = $modified;
293
+        $changed['deleted'] = $deleted;
294
+        return $changed;
295
+    }
296 296
 
297
-	private function isFederation(): bool {
298
-		if ($this->trustedServers === null || $this->request === null) {
299
-			return false;
300
-		}
297
+    private function isFederation(): bool {
298
+        if ($this->trustedServers === null || $this->request === null) {
299
+            return false;
300
+        }
301 301
 
302
-		/** @psalm-suppress NoInterfaceProperties */
303
-		$server = $this->request->server;
304
-		if (!isset($server['PHP_AUTH_USER']) || $server['PHP_AUTH_USER'] !== 'system') {
305
-			return false;
306
-		}
302
+        /** @psalm-suppress NoInterfaceProperties */
303
+        $server = $this->request->server;
304
+        if (!isset($server['PHP_AUTH_USER']) || $server['PHP_AUTH_USER'] !== 'system') {
305
+            return false;
306
+        }
307 307
 
308
-		/** @psalm-suppress NoInterfaceProperties */
309
-		$sharedSecret = $server['PHP_AUTH_PW'] ?? null;
310
-		if ($sharedSecret === null) {
311
-			return false;
312
-		}
308
+        /** @psalm-suppress NoInterfaceProperties */
309
+        $sharedSecret = $server['PHP_AUTH_PW'] ?? null;
310
+        if ($sharedSecret === null) {
311
+            return false;
312
+        }
313 313
 
314
-		$servers = $this->trustedServers->getServers();
315
-		$trusted = array_filter($servers, function ($trustedServer) use ($sharedSecret) {
316
-			return $trustedServer['shared_secret'] === $sharedSecret;
317
-		});
318
-		// Authentication is fine, but it's not for a federated share
319
-		if (empty($trusted)) {
320
-			return false;
321
-		}
314
+        $servers = $this->trustedServers->getServers();
315
+        $trusted = array_filter($servers, function ($trustedServer) use ($sharedSecret) {
316
+            return $trustedServer['shared_secret'] === $sharedSecret;
317
+        });
318
+        // Authentication is fine, but it's not for a federated share
319
+        if (empty($trusted)) {
320
+            return false;
321
+        }
322 322
 
323
-		return true;
324
-	}
323
+        return true;
324
+    }
325 325
 
326
-	/**
327
-	 * If the validation doesn't work the card is "not found" so we
328
-	 * return empty carddata even if the carddata might exist in the local backend.
329
-	 * This can happen when a user sets the required properties
330
-	 * FN, N to a local scope only but the request is from
331
-	 * a federated share.
332
-	 *
333
-	 * @see https://github.com/nextcloud/server/issues/38042
334
-	 *
335
-	 * @param array $obj
336
-	 * @return string|null
337
-	 */
338
-	private function extractCarddata(array $obj): ?string {
339
-		$obj['acl'] = $this->getChildACL();
340
-		$cardData = $obj['carddata'];
341
-		/** @var VCard $vCard */
342
-		$vCard = Reader::read($cardData);
343
-		foreach ($vCard->children() as $child) {
344
-			$scope = $child->offsetGet('X-NC-SCOPE');
345
-			if ($scope !== null && $scope->getValue() === IAccountManager::SCOPE_LOCAL) {
346
-				$vCard->remove($child);
347
-			}
348
-		}
349
-		$messages = $vCard->validate();
350
-		if (!empty($messages)) {
351
-			return null;
352
-		}
326
+    /**
327
+     * If the validation doesn't work the card is "not found" so we
328
+     * return empty carddata even if the carddata might exist in the local backend.
329
+     * This can happen when a user sets the required properties
330
+     * FN, N to a local scope only but the request is from
331
+     * a federated share.
332
+     *
333
+     * @see https://github.com/nextcloud/server/issues/38042
334
+     *
335
+     * @param array $obj
336
+     * @return string|null
337
+     */
338
+    private function extractCarddata(array $obj): ?string {
339
+        $obj['acl'] = $this->getChildACL();
340
+        $cardData = $obj['carddata'];
341
+        /** @var VCard $vCard */
342
+        $vCard = Reader::read($cardData);
343
+        foreach ($vCard->children() as $child) {
344
+            $scope = $child->offsetGet('X-NC-SCOPE');
345
+            if ($scope !== null && $scope->getValue() === IAccountManager::SCOPE_LOCAL) {
346
+                $vCard->remove($child);
347
+            }
348
+        }
349
+        $messages = $vCard->validate();
350
+        if (!empty($messages)) {
351
+            return null;
352
+        }
353 353
 
354
-		return $vCard->serialize();
355
-	}
354
+        return $vCard->serialize();
355
+    }
356 356
 
357
-	/**
358
-	 * @return mixed
359
-	 * @throws Forbidden
360
-	 */
361
-	public function delete() {
362
-		if ($this->isFederation()) {
363
-			parent::delete();
364
-		}
365
-		throw new Forbidden();
366
-	}
357
+    /**
358
+     * @return mixed
359
+     * @throws Forbidden
360
+     */
361
+    public function delete() {
362
+        if ($this->isFederation()) {
363
+            parent::delete();
364
+        }
365
+        throw new Forbidden();
366
+    }
367 367
 
368
-	public function getACL() {
369
-		return array_filter(parent::getACL(), function ($acl) {
370
-			if (in_array($acl['privilege'], ['{DAV:}write', '{DAV:}all'], true)) {
371
-				return false;
372
-			}
373
-			return true;
374
-		});
375
-	}
368
+    public function getACL() {
369
+        return array_filter(parent::getACL(), function ($acl) {
370
+            if (in_array($acl['privilege'], ['{DAV:}write', '{DAV:}all'], true)) {
371
+                return false;
372
+            }
373
+            return true;
374
+        });
375
+    }
376 376
 }
Please login to merge, or discard this patch.