Passed
Push — master ( 8383b6...4175c3 )
by Roeland
13:28 queued 11s
created
apps/dav/lib/CardDAV/SyncService.php 2 patches
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
 			if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) {
97 97
 				// remote server revoked access to the address book, remove it
98 98
 				$this->backend->deleteAddressBook($addressBookId);
99
-				$this->logger->info('Authorization failed, remove address book: ' . $url, ['app' => 'dav']);
99
+				$this->logger->info('Authorization failed, remove address book: '.$url, ['app' => 'dav']);
100 100
 				throw $ex;
101 101
 			}
102 102
 		}
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
 	 */
170 170
 	protected function getClient($url, $userName, $sharedSecret) {
171 171
 		$settings = [
172
-			'baseUri' => $url . '/',
172
+			'baseUri' => $url.'/',
173 173
 			'userName' => $userName,
174 174
 			'password' => $sharedSecret,
175 175
 		];
@@ -306,7 +306,7 @@  discard block
 block discarded – undo
306 306
 		if (is_null($this->localSystemAddressBook)) {
307 307
 			$systemPrincipal = "principals/system/system";
308 308
 			$this->localSystemAddressBook = $this->ensureSystemAddressBookExists($systemPrincipal, 'system', [
309
-				'{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
309
+				'{'.Plugin::NS_CARDDAV.'}addressbook-description' => 'System addressbook which holds all users of this instance'
310 310
 			]);
311 311
 		}
312 312
 
@@ -315,7 +315,7 @@  discard block
 block discarded – undo
315 315
 
316 316
 	public function syncInstance(\Closure $progressCallback = null) {
317 317
 		$systemAddressBook = $this->getLocalSystemAddressBook();
318
-		$this->userManager->callForAllUsers(function ($user) use ($systemAddressBook, $progressCallback) {
318
+		$this->userManager->callForAllUsers(function($user) use ($systemAddressBook, $progressCallback) {
319 319
 			$this->updateUser($user);
320 320
 			if (!is_null($progressCallback)) {
321 321
 				$progressCallback();
Please login to merge, or discard this patch.
Indentation   +297 added lines, -297 removed lines patch added patch discarded remove patch
@@ -43,301 +43,301 @@
 block discarded – undo
43 43
 
44 44
 class SyncService {
45 45
 
46
-	/** @var CardDavBackend */
47
-	private $backend;
48
-
49
-	/** @var IUserManager */
50
-	private $userManager;
51
-
52
-	/** @var ILogger */
53
-	private $logger;
54
-
55
-	/** @var array */
56
-	private $localSystemAddressBook;
57
-
58
-	/** @var AccountManager */
59
-	private $accountManager;
60
-
61
-	/** @var string */
62
-	protected $certPath;
63
-
64
-	/**
65
-	 * SyncService constructor.
66
-	 *
67
-	 * @param CardDavBackend $backend
68
-	 * @param IUserManager $userManager
69
-	 * @param ILogger $logger
70
-	 * @param AccountManager $accountManager
71
-	 */
72
-	public function __construct(CardDavBackend $backend, IUserManager $userManager, ILogger $logger, AccountManager $accountManager) {
73
-		$this->backend = $backend;
74
-		$this->userManager = $userManager;
75
-		$this->logger = $logger;
76
-		$this->accountManager = $accountManager;
77
-		$this->certPath = '';
78
-	}
79
-
80
-	/**
81
-	 * @param string $url
82
-	 * @param string $userName
83
-	 * @param string $addressBookUrl
84
-	 * @param string $sharedSecret
85
-	 * @param string $syncToken
86
-	 * @param int $targetBookId
87
-	 * @param string $targetPrincipal
88
-	 * @param array $targetProperties
89
-	 * @return string
90
-	 * @throws \Exception
91
-	 */
92
-	public function syncRemoteAddressBook($url, $userName, $addressBookUrl, $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetProperties) {
93
-		// 1. create addressbook
94
-		$book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetProperties);
95
-		$addressBookId = $book['id'];
96
-
97
-		// 2. query changes
98
-		try {
99
-			$response = $this->requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken);
100
-		} catch (ClientHttpException $ex) {
101
-			if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) {
102
-				// remote server revoked access to the address book, remove it
103
-				$this->backend->deleteAddressBook($addressBookId);
104
-				$this->logger->info('Authorization failed, remove address book: ' . $url, ['app' => 'dav']);
105
-				throw $ex;
106
-			}
107
-		}
108
-
109
-		// 3. apply changes
110
-		// TODO: use multi-get for download
111
-		foreach ($response['response'] as $resource => $status) {
112
-			$cardUri = basename($resource);
113
-			if (isset($status[200])) {
114
-				$vCard = $this->download($url, $userName, $sharedSecret, $resource);
115
-				$existingCard = $this->backend->getCard($addressBookId, $cardUri);
116
-				if ($existingCard === false) {
117
-					$this->backend->createCard($addressBookId, $cardUri, $vCard['body']);
118
-				} else {
119
-					$this->backend->updateCard($addressBookId, $cardUri, $vCard['body']);
120
-				}
121
-			} else {
122
-				$this->backend->deleteCard($addressBookId, $cardUri);
123
-			}
124
-		}
125
-
126
-		return $response['token'];
127
-	}
128
-
129
-	/**
130
-	 * @param string $principal
131
-	 * @param string $id
132
-	 * @param array $properties
133
-	 * @return array|null
134
-	 * @throws \Sabre\DAV\Exception\BadRequest
135
-	 */
136
-	public function ensureSystemAddressBookExists($principal, $id, $properties) {
137
-		$book = $this->backend->getAddressBooksByUri($principal, $id);
138
-		if (!is_null($book)) {
139
-			return $book;
140
-		}
141
-		$this->backend->createAddressBook($principal, $id, $properties);
142
-
143
-		return $this->backend->getAddressBooksByUri($principal, $id);
144
-	}
145
-
146
-	/**
147
-	 * Check if there is a valid certPath we should use
148
-	 *
149
-	 * @return string
150
-	 */
151
-	protected function getCertPath() {
152
-
153
-		// we already have a valid certPath
154
-		if ($this->certPath !== '') {
155
-			return $this->certPath;
156
-		}
157
-
158
-		/** @var ICertificateManager $certManager */
159
-		$certManager = \OC::$server->getCertificateManager(null);
160
-		$certPath = $certManager->getAbsoluteBundlePath();
161
-		if (file_exists($certPath)) {
162
-			$this->certPath = $certPath;
163
-		}
164
-
165
-		return $this->certPath;
166
-	}
167
-
168
-	/**
169
-	 * @param string $url
170
-	 * @param string $userName
171
-	 * @param string $addressBookUrl
172
-	 * @param string $sharedSecret
173
-	 * @return Client
174
-	 */
175
-	protected function getClient($url, $userName, $sharedSecret) {
176
-		$settings = [
177
-			'baseUri' => $url . '/',
178
-			'userName' => $userName,
179
-			'password' => $sharedSecret,
180
-		];
181
-		$client = new Client($settings);
182
-		$certPath = $this->getCertPath();
183
-		$client->setThrowExceptions(true);
184
-
185
-		if ($certPath !== '' && strpos($url, 'http://') !== 0) {
186
-			$client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
187
-		}
188
-
189
-		return $client;
190
-	}
191
-
192
-	/**
193
-	 * @param string $url
194
-	 * @param string $userName
195
-	 * @param string $addressBookUrl
196
-	 * @param string $sharedSecret
197
-	 * @param string $syncToken
198
-	 * @return array
199
-	 */
200
-	protected function requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken) {
201
-		$client = $this->getClient($url, $userName, $sharedSecret);
202
-
203
-		$body = $this->buildSyncCollectionRequestBody($syncToken);
204
-
205
-		$response = $client->request('REPORT', $addressBookUrl, $body, [
206
-			'Content-Type' => 'application/xml'
207
-		]);
208
-
209
-		return $this->parseMultiStatus($response['body']);
210
-	}
211
-
212
-	/**
213
-	 * @param string $url
214
-	 * @param string $userName
215
-	 * @param string $sharedSecret
216
-	 * @param string $resourcePath
217
-	 * @return array
218
-	 */
219
-	protected function download($url, $userName, $sharedSecret, $resourcePath) {
220
-		$client = $this->getClient($url, $userName, $sharedSecret);
221
-		return $client->request('GET', $resourcePath);
222
-	}
223
-
224
-	/**
225
-	 * @param string|null $syncToken
226
-	 * @return string
227
-	 */
228
-	private function buildSyncCollectionRequestBody($syncToken) {
229
-		$dom = new \DOMDocument('1.0', 'UTF-8');
230
-		$dom->formatOutput = true;
231
-		$root = $dom->createElementNS('DAV:', 'd:sync-collection');
232
-		$sync = $dom->createElement('d:sync-token', $syncToken);
233
-		$prop = $dom->createElement('d:prop');
234
-		$cont = $dom->createElement('d:getcontenttype');
235
-		$etag = $dom->createElement('d:getetag');
236
-
237
-		$prop->appendChild($cont);
238
-		$prop->appendChild($etag);
239
-		$root->appendChild($sync);
240
-		$root->appendChild($prop);
241
-		$dom->appendChild($root);
242
-		return $dom->saveXML();
243
-	}
244
-
245
-	/**
246
-	 * @param string $body
247
-	 * @return array
248
-	 * @throws \Sabre\Xml\ParseException
249
-	 */
250
-	private function parseMultiStatus($body) {
251
-		$xml = new Service();
252
-
253
-		/** @var MultiStatus $multiStatus */
254
-		$multiStatus = $xml->expect('{DAV:}multistatus', $body);
255
-
256
-		$result = [];
257
-		foreach ($multiStatus->getResponses() as $response) {
258
-			$result[$response->getHref()] = $response->getResponseProperties();
259
-		}
260
-
261
-		return ['response' => $result, 'token' => $multiStatus->getSyncToken()];
262
-	}
263
-
264
-	/**
265
-	 * @param IUser $user
266
-	 */
267
-	public function updateUser(IUser $user) {
268
-		$systemAddressBook = $this->getLocalSystemAddressBook();
269
-		$addressBookId = $systemAddressBook['id'];
270
-		$converter = new Converter($this->accountManager);
271
-		$name = $user->getBackendClassName();
272
-		$userId = $user->getUID();
273
-
274
-		$cardId = "$name:$userId.vcf";
275
-		$card = $this->backend->getCard($addressBookId, $cardId);
276
-		if ($user->isEnabled()) {
277
-			if ($card === false) {
278
-				$vCard = $converter->createCardFromUser($user);
279
-				if ($vCard !== null) {
280
-					$this->backend->createCard($addressBookId, $cardId, $vCard->serialize());
281
-				}
282
-			} else {
283
-				$vCard = $converter->createCardFromUser($user);
284
-				if (is_null($vCard)) {
285
-					$this->backend->deleteCard($addressBookId, $cardId);
286
-				} else {
287
-					$this->backend->updateCard($addressBookId, $cardId, $vCard->serialize());
288
-				}
289
-			}
290
-		} else {
291
-			$this->backend->deleteCard($addressBookId, $cardId);
292
-		}
293
-	}
294
-
295
-	/**
296
-	 * @param IUser|string $userOrCardId
297
-	 */
298
-	public function deleteUser($userOrCardId) {
299
-		$systemAddressBook = $this->getLocalSystemAddressBook();
300
-		if ($userOrCardId instanceof IUser) {
301
-			$name = $userOrCardId->getBackendClassName();
302
-			$userId = $userOrCardId->getUID();
303
-
304
-			$userOrCardId = "$name:$userId.vcf";
305
-		}
306
-		$this->backend->deleteCard($systemAddressBook['id'], $userOrCardId);
307
-	}
308
-
309
-	/**
310
-	 * @return array|null
311
-	 */
312
-	public function getLocalSystemAddressBook() {
313
-		if (is_null($this->localSystemAddressBook)) {
314
-			$systemPrincipal = "principals/system/system";
315
-			$this->localSystemAddressBook = $this->ensureSystemAddressBookExists($systemPrincipal, 'system', [
316
-				'{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
317
-			]);
318
-		}
319
-
320
-		return $this->localSystemAddressBook;
321
-	}
322
-
323
-	public function syncInstance(\Closure $progressCallback = null) {
324
-		$systemAddressBook = $this->getLocalSystemAddressBook();
325
-		$this->userManager->callForAllUsers(function ($user) use ($systemAddressBook, $progressCallback) {
326
-			$this->updateUser($user);
327
-			if (!is_null($progressCallback)) {
328
-				$progressCallback();
329
-			}
330
-		});
331
-
332
-		// remove no longer existing
333
-		$allCards = $this->backend->getCards($systemAddressBook['id']);
334
-		foreach ($allCards as $card) {
335
-			$vCard = Reader::read($card['carddata']);
336
-			$uid = $vCard->UID->getValue();
337
-			// load backend and see if user exists
338
-			if (!$this->userManager->userExists($uid)) {
339
-				$this->deleteUser($card['uri']);
340
-			}
341
-		}
342
-	}
46
+    /** @var CardDavBackend */
47
+    private $backend;
48
+
49
+    /** @var IUserManager */
50
+    private $userManager;
51
+
52
+    /** @var ILogger */
53
+    private $logger;
54
+
55
+    /** @var array */
56
+    private $localSystemAddressBook;
57
+
58
+    /** @var AccountManager */
59
+    private $accountManager;
60
+
61
+    /** @var string */
62
+    protected $certPath;
63
+
64
+    /**
65
+     * SyncService constructor.
66
+     *
67
+     * @param CardDavBackend $backend
68
+     * @param IUserManager $userManager
69
+     * @param ILogger $logger
70
+     * @param AccountManager $accountManager
71
+     */
72
+    public function __construct(CardDavBackend $backend, IUserManager $userManager, ILogger $logger, AccountManager $accountManager) {
73
+        $this->backend = $backend;
74
+        $this->userManager = $userManager;
75
+        $this->logger = $logger;
76
+        $this->accountManager = $accountManager;
77
+        $this->certPath = '';
78
+    }
79
+
80
+    /**
81
+     * @param string $url
82
+     * @param string $userName
83
+     * @param string $addressBookUrl
84
+     * @param string $sharedSecret
85
+     * @param string $syncToken
86
+     * @param int $targetBookId
87
+     * @param string $targetPrincipal
88
+     * @param array $targetProperties
89
+     * @return string
90
+     * @throws \Exception
91
+     */
92
+    public function syncRemoteAddressBook($url, $userName, $addressBookUrl, $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetProperties) {
93
+        // 1. create addressbook
94
+        $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetProperties);
95
+        $addressBookId = $book['id'];
96
+
97
+        // 2. query changes
98
+        try {
99
+            $response = $this->requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken);
100
+        } catch (ClientHttpException $ex) {
101
+            if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) {
102
+                // remote server revoked access to the address book, remove it
103
+                $this->backend->deleteAddressBook($addressBookId);
104
+                $this->logger->info('Authorization failed, remove address book: ' . $url, ['app' => 'dav']);
105
+                throw $ex;
106
+            }
107
+        }
108
+
109
+        // 3. apply changes
110
+        // TODO: use multi-get for download
111
+        foreach ($response['response'] as $resource => $status) {
112
+            $cardUri = basename($resource);
113
+            if (isset($status[200])) {
114
+                $vCard = $this->download($url, $userName, $sharedSecret, $resource);
115
+                $existingCard = $this->backend->getCard($addressBookId, $cardUri);
116
+                if ($existingCard === false) {
117
+                    $this->backend->createCard($addressBookId, $cardUri, $vCard['body']);
118
+                } else {
119
+                    $this->backend->updateCard($addressBookId, $cardUri, $vCard['body']);
120
+                }
121
+            } else {
122
+                $this->backend->deleteCard($addressBookId, $cardUri);
123
+            }
124
+        }
125
+
126
+        return $response['token'];
127
+    }
128
+
129
+    /**
130
+     * @param string $principal
131
+     * @param string $id
132
+     * @param array $properties
133
+     * @return array|null
134
+     * @throws \Sabre\DAV\Exception\BadRequest
135
+     */
136
+    public function ensureSystemAddressBookExists($principal, $id, $properties) {
137
+        $book = $this->backend->getAddressBooksByUri($principal, $id);
138
+        if (!is_null($book)) {
139
+            return $book;
140
+        }
141
+        $this->backend->createAddressBook($principal, $id, $properties);
142
+
143
+        return $this->backend->getAddressBooksByUri($principal, $id);
144
+    }
145
+
146
+    /**
147
+     * Check if there is a valid certPath we should use
148
+     *
149
+     * @return string
150
+     */
151
+    protected function getCertPath() {
152
+
153
+        // we already have a valid certPath
154
+        if ($this->certPath !== '') {
155
+            return $this->certPath;
156
+        }
157
+
158
+        /** @var ICertificateManager $certManager */
159
+        $certManager = \OC::$server->getCertificateManager(null);
160
+        $certPath = $certManager->getAbsoluteBundlePath();
161
+        if (file_exists($certPath)) {
162
+            $this->certPath = $certPath;
163
+        }
164
+
165
+        return $this->certPath;
166
+    }
167
+
168
+    /**
169
+     * @param string $url
170
+     * @param string $userName
171
+     * @param string $addressBookUrl
172
+     * @param string $sharedSecret
173
+     * @return Client
174
+     */
175
+    protected function getClient($url, $userName, $sharedSecret) {
176
+        $settings = [
177
+            'baseUri' => $url . '/',
178
+            'userName' => $userName,
179
+            'password' => $sharedSecret,
180
+        ];
181
+        $client = new Client($settings);
182
+        $certPath = $this->getCertPath();
183
+        $client->setThrowExceptions(true);
184
+
185
+        if ($certPath !== '' && strpos($url, 'http://') !== 0) {
186
+            $client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
187
+        }
188
+
189
+        return $client;
190
+    }
191
+
192
+    /**
193
+     * @param string $url
194
+     * @param string $userName
195
+     * @param string $addressBookUrl
196
+     * @param string $sharedSecret
197
+     * @param string $syncToken
198
+     * @return array
199
+     */
200
+    protected function requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken) {
201
+        $client = $this->getClient($url, $userName, $sharedSecret);
202
+
203
+        $body = $this->buildSyncCollectionRequestBody($syncToken);
204
+
205
+        $response = $client->request('REPORT', $addressBookUrl, $body, [
206
+            'Content-Type' => 'application/xml'
207
+        ]);
208
+
209
+        return $this->parseMultiStatus($response['body']);
210
+    }
211
+
212
+    /**
213
+     * @param string $url
214
+     * @param string $userName
215
+     * @param string $sharedSecret
216
+     * @param string $resourcePath
217
+     * @return array
218
+     */
219
+    protected function download($url, $userName, $sharedSecret, $resourcePath) {
220
+        $client = $this->getClient($url, $userName, $sharedSecret);
221
+        return $client->request('GET', $resourcePath);
222
+    }
223
+
224
+    /**
225
+     * @param string|null $syncToken
226
+     * @return string
227
+     */
228
+    private function buildSyncCollectionRequestBody($syncToken) {
229
+        $dom = new \DOMDocument('1.0', 'UTF-8');
230
+        $dom->formatOutput = true;
231
+        $root = $dom->createElementNS('DAV:', 'd:sync-collection');
232
+        $sync = $dom->createElement('d:sync-token', $syncToken);
233
+        $prop = $dom->createElement('d:prop');
234
+        $cont = $dom->createElement('d:getcontenttype');
235
+        $etag = $dom->createElement('d:getetag');
236
+
237
+        $prop->appendChild($cont);
238
+        $prop->appendChild($etag);
239
+        $root->appendChild($sync);
240
+        $root->appendChild($prop);
241
+        $dom->appendChild($root);
242
+        return $dom->saveXML();
243
+    }
244
+
245
+    /**
246
+     * @param string $body
247
+     * @return array
248
+     * @throws \Sabre\Xml\ParseException
249
+     */
250
+    private function parseMultiStatus($body) {
251
+        $xml = new Service();
252
+
253
+        /** @var MultiStatus $multiStatus */
254
+        $multiStatus = $xml->expect('{DAV:}multistatus', $body);
255
+
256
+        $result = [];
257
+        foreach ($multiStatus->getResponses() as $response) {
258
+            $result[$response->getHref()] = $response->getResponseProperties();
259
+        }
260
+
261
+        return ['response' => $result, 'token' => $multiStatus->getSyncToken()];
262
+    }
263
+
264
+    /**
265
+     * @param IUser $user
266
+     */
267
+    public function updateUser(IUser $user) {
268
+        $systemAddressBook = $this->getLocalSystemAddressBook();
269
+        $addressBookId = $systemAddressBook['id'];
270
+        $converter = new Converter($this->accountManager);
271
+        $name = $user->getBackendClassName();
272
+        $userId = $user->getUID();
273
+
274
+        $cardId = "$name:$userId.vcf";
275
+        $card = $this->backend->getCard($addressBookId, $cardId);
276
+        if ($user->isEnabled()) {
277
+            if ($card === false) {
278
+                $vCard = $converter->createCardFromUser($user);
279
+                if ($vCard !== null) {
280
+                    $this->backend->createCard($addressBookId, $cardId, $vCard->serialize());
281
+                }
282
+            } else {
283
+                $vCard = $converter->createCardFromUser($user);
284
+                if (is_null($vCard)) {
285
+                    $this->backend->deleteCard($addressBookId, $cardId);
286
+                } else {
287
+                    $this->backend->updateCard($addressBookId, $cardId, $vCard->serialize());
288
+                }
289
+            }
290
+        } else {
291
+            $this->backend->deleteCard($addressBookId, $cardId);
292
+        }
293
+    }
294
+
295
+    /**
296
+     * @param IUser|string $userOrCardId
297
+     */
298
+    public function deleteUser($userOrCardId) {
299
+        $systemAddressBook = $this->getLocalSystemAddressBook();
300
+        if ($userOrCardId instanceof IUser) {
301
+            $name = $userOrCardId->getBackendClassName();
302
+            $userId = $userOrCardId->getUID();
303
+
304
+            $userOrCardId = "$name:$userId.vcf";
305
+        }
306
+        $this->backend->deleteCard($systemAddressBook['id'], $userOrCardId);
307
+    }
308
+
309
+    /**
310
+     * @return array|null
311
+     */
312
+    public function getLocalSystemAddressBook() {
313
+        if (is_null($this->localSystemAddressBook)) {
314
+            $systemPrincipal = "principals/system/system";
315
+            $this->localSystemAddressBook = $this->ensureSystemAddressBookExists($systemPrincipal, 'system', [
316
+                '{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
317
+            ]);
318
+        }
319
+
320
+        return $this->localSystemAddressBook;
321
+    }
322
+
323
+    public function syncInstance(\Closure $progressCallback = null) {
324
+        $systemAddressBook = $this->getLocalSystemAddressBook();
325
+        $this->userManager->callForAllUsers(function ($user) use ($systemAddressBook, $progressCallback) {
326
+            $this->updateUser($user);
327
+            if (!is_null($progressCallback)) {
328
+                $progressCallback();
329
+            }
330
+        });
331
+
332
+        // remove no longer existing
333
+        $allCards = $this->backend->getCards($systemAddressBook['id']);
334
+        foreach ($allCards as $card) {
335
+            $vCard = Reader::read($card['carddata']);
336
+            $uid = $vCard->UID->getValue();
337
+            // load backend and see if user exists
338
+            if (!$this->userManager->userExists($uid)) {
339
+                $this->deleteUser($card['uri']);
340
+            }
341
+        }
342
+    }
343 343
 }
Please login to merge, or discard this patch.