Completed
Pull Request — master (#347)
by Maxence
01:42
created

DavService::manageDavCard()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
4
/**
5
 * Circles - Bring cloud-users closer together.
6
 *
7
 * This file is licensed under the Affero General Public License version 3 or
8
 * later. See the COPYING file.
9
 *
10
 * @author Maxence Lange <[email protected]>
11
 * @copyright 2017
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
30
namespace OCA\Circles\Service;
31
32
33
use OCA\Circles\Db\CirclesRequest;
34
use OCA\Circles\Db\MembersRequest;
35
use OCA\Circles\Exceptions\CircleAlreadyExistsException;
36
use OCA\Circles\Exceptions\CircleDoesNotExistException;
37
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
38
use OCA\Circles\Exceptions\MemberDoesNotExistException;
39
use OCA\Circles\Exceptions\NotLocalMemberException;
40
use OCA\Circles\Model\Circle;
41
use OCA\Circles\Model\DavCard;
42
use OCA\Circles\Model\Member;
43
use OCA\DAV\CardDAV\CardDavBackend;
44
use OCP\App\ManagerEvent;
45
use OCP\IUserManager;
46
use Symfony\Component\EventDispatcher\GenericEvent;
47
48
49
/**
50
 * Class DavService
51
 *
52
 * @package OCA\Circles\Service
53
 */
54
class DavService {
55
56
57
	/** @var string */
58
	private $userId;
59
60
	/** @var IUserManager */
61
	private $userManager;
62
63
	/** @var CardDavBackend */
64
	private $cardDavBackend;
65
66
	/** @var CirclesRequest */
67
	private $circlesRequest;
68
69
	/** @var MembersRequest */
70
	private $membersRequest;
71
72
	/** @var ConfigService */
73
	private $configService;
74
75
	/** @var MiscService */
76
	private $miscService;
77
78
79
	/** @var array */
80
	private $migratedBooks = [];
81
82
83
	/**
84
	 * TimezoneService constructor.
85
	 *
86
	 * @param string $userId
87
	 * @param IUserManager $userManager
88
	 * @param CirclesRequest $circlesRequest
89
	 * @param MembersRequest $membersRequest
90
	 * @param ConfigService $configService
91
	 * @param MiscService $miscService
92
	 */
93 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
		$userId, IUserManager $userManager, CardDavBackend $cardDavBackend, CirclesRequest $circlesRequest,
95
		MembersRequest $membersRequest, ConfigService $configService, MiscService $miscService
96
	) {
97
		$this->userId = $userId;
98
		$this->userManager = $userManager;
99
		$this->cardDavBackend = $cardDavBackend;
100
		$this->circlesRequest = $circlesRequest;
101
		$this->membersRequest = $membersRequest;
102
		$this->configService = $configService;
103
		$this->miscService = $miscService;
104
	}
105
106
107
	/**
108
	 * @param ManagerEvent $event
109
	 */
110
	public function onAppEnabled(ManagerEvent $event) {
111
		if ($event->getAppID() !== 'circles') {
112
			return;
113
		}
114
115
		$this->migration();
116
	}
117
118
119
	/**
120
	 * @param GenericEvent $event
121
	 */
122
	public function onCreateCard(GenericEvent $event) {
123
		$davCard = $this->generateDavCard($event);
124
		$this->manageDavCard($davCard);
125
	}
126
127
128
	/**
129
	 * @param GenericEvent $event
130
	 */
131
	public function onUpdateCard(GenericEvent $event) {
132
		$davCard = $this->generateDavCard($event);
133
		$this->manageDavCard($davCard);
134
	}
135
136
137
	/**
138
	 * @param GenericEvent $event
139
	 */
140
	public function onDeleteCard(GenericEvent $event) {
141
		$addressBookId = $event->getArgument('addressBookId');
0 ignored issues
show
Unused Code introduced by
$addressBookId is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
142
		$cardUri = $event->getArgument('cardUri');
0 ignored issues
show
Unused Code introduced by
$cardUri is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
143
	}
144
145
146
	/**
147
	 * @param GenericEvent $event
148
	 *
149
	 * @return DavCard
150
	 */
151
	private function generateDavCard(GenericEvent $event): DavCard {
152
		$addressBookId = $event->getArgument('addressBookId');
153
		$cardUri = $event->getArgument('cardUri');
154
		$cardData = $event->getArgument('cardData');
155
156
		$davCard = new DavCard();
157
		$davCard->setOwner($this->getOwnerFromAddressBook($addressBookId));
158
		$davCard->importFromDav($cardData);
159
		$davCard->setAddressBookId($addressBookId);
160
		$davCard->setCardUri($cardUri);
161
162
		return $davCard;
163
	}
164
165
166
	/**
167
	 * @param DavCard $davCard
168
	 */
169
	private function manageDavCard(DavCard $davCard) {
170
		$this->miscService->log('Updating Card: ' . json_encode($davCard));
171
		$this->manageCircles($davCard);
172
		$this->manageContact($davCard);
173
	}
174
175
176
	/**
177
	 * @param DavCard $davCard
178
	 */
179
	private function manageContact(DavCard $davCard) {
180
		$this->manageDeprecatedMembers($davCard);
181
182
		try {
183
			$userId = $this->isLocalMember($davCard);
184
185
			$this->manageLocalContact($davCard, $userId);
186
		} catch (NotLocalMemberException $e) {
187
			$this->manageRemoteContact($davCard);
188
		}
189
	}
190
191
192
	/**
193
	 * @param DavCard $davCard
194
	 */
195
	private function manageDeprecatedMembers(DavCard $davCard) {
196
		$circles = array_map(
197
			function(Circle $circle) {
198
				return $circle->getUniqueId();
199
			}, $davCard->getCircles()
200
		);
201
202
		$members = $this->membersRequest->getMembersByContactId($davCard->getContactId());
203
		foreach ($members as $member) {
204
			if (!in_array($member->getCircleId(), $circles)) {
205
				$this->membersRequest->removeMember($member);
206
			}
207
		}
208
	}
209
210
211
	/**
212
	 * @param DavCard $davCard
213
	 * @param string $userId
214
	 */
215
	private function manageLocalContact(DavCard $davCard, string $userId) {
216
		foreach ($davCard->getCircles() as $circle) {
217
			$this->manageMember($circle, $davCard, Member::TYPE_USER, $userId);
218
		}
219
220
	}
221
222
223
	/**
224
	 * @param DavCard $davCard
225
	 */
226
	private function manageRemoteContact(DavCard $davCard) {
227
		foreach ($davCard->getCircles() as $circle) {
228
			$this->manageMember($circle, $davCard, Member::TYPE_CONTACT);
229
		}
230
	}
231
232
233
	/**
234
	 * @param Circle $circle
235
	 * @param DavCard $davCard
236
	 * @param int $type
237
	 * @param string $userId
238
	 */
239
	private function manageMember(Circle $circle, DavCard $davCard, int $type, string $userId = '') {
240
		if ($userId === '') {
241
			$userId = $davCard->getOwner() . ':' . $davCard->getContactId();
242
		}
243
244
		try {
245
			$member =
246
				$this->membersRequest->getContactMember($circle->getUniqueId(), $davCard->getContactId());
247
248
			if ($member->getType() !== $type) {
249
				$this->membersRequest->removeMember($member);
250
				throw new MemberDoesNotExistException();
251
			}
252
		} catch (MemberDoesNotExistException $e) {
253
			$member = new Member();
254
			$member->setLevel(Member::LEVEL_MEMBER);
255
			$member->setStatus(Member::STATUS_MEMBER);
256
			$member->setContactId($davCard->getContactId());
257
			$member->setType($type);
258
			$member->setCircleId($circle->getUniqueId());
259
			$member->setUserId($userId);
260
261
			try {
262
				$this->membersRequest->createMember($member);
263
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
264
			}
265
		}
266
	}
267
268
269
	/**
270
	 * @param DavCard $davCard
271
	 *
272
	 * @return string
273
	 * @throws NotLocalMemberException
274
	 */
275
	private function isLocalMember(DavCard $davCard): string {
276
		foreach ($davCard->getEmails() as $email) {
277
			if (strpos($email, '@') === false) {
278
				continue;
279
			}
280
281
			list ($username, $domain) = explode('@', $email);
282
			if (in_array($domain, $this->configService->getAvailableHosts())) {
283
				$user = $this->userManager->get($username);
284
				if ($user !== null) {
285
					return $user->getUID();
286
				}
287
			}
288
		}
289
290
		throw new NotLocalMemberException();
291
	}
292
293
294
	/**
295
	 * @param DavCard $davCard
296
	 */
297
	private function manageCircles(DavCard $davCard) {
298
		$fromCard = $davCard->getGroups();
299
		$current = array_map(
300
			function(Circle $circle) {
301
				return $circle->getContactGroupName();
302
			}, $this->getCirclesFromBook($davCard->getAddressBookId())
303
		);
304
305
		$this->manageNewCircles($davCard, $fromCard, $current);
306
		$this->manageDeprecatedCircles($fromCard, $current);
0 ignored issues
show
Unused Code introduced by
The call to the method OCA\Circles\Service\DavS...nageDeprecatedCircles() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
307
308
		$this->assignCirclesToCard($davCard);
309
	}
310
311
312
	/**
313
	 * @param DavCard $davCard
314
	 * @param array $fromCard
315
	 * @param array $current
316
	 */
317
	private function manageNewCircles(DavCard $davCard, array $fromCard, array $current) {
318
		foreach ($fromCard as $group) {
319
			if (in_array($group, $current)) {
320
				continue;
321
			}
322
323
			$circle = new Circle(Circle::CIRCLES_PUBLIC, $group . ' - ' . $this->uuid(5));
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Service\DavService::uuid() has been deprecated.

This method has been deprecated.

Loading history...
324
			$circle->setContactAddressBook($davCard->getAddressBookId());
325
			$circle->setContactGroupName($group);
326
327
			try {
328
				$this->circlesRequest->createCircle($circle, $davCard->getOwner());
329
				$member = new Member($davCard->getOwner(), Member::TYPE_USER, $circle->getUniqueId());
330
				$member->setLevel(Member::LEVEL_OWNER);
331
				$member->setStatus(Member::STATUS_MEMBER);
332
333
				try {
334
					$this->membersRequest->createMember($member);
335
				} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
336
				}
337
			} catch (CircleAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
338
			}
339
340
		}
341
	}
342
343
344
	/**
345
	 * // TODO: Get all group from an addressbook
346
	 * // TODO: remove deprecated circles
347
	 *
348
	 * @param array $fromCard
349
	 * @param array $current
350
	 */
351
	private function manageDeprecatedCircles(array $fromCard, array $current) {
0 ignored issues
show
Unused Code introduced by
The parameter $fromCard is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $current is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
352
	}
353
354
355
	/**
356
	 * @param DavCard $davCard
357
	 */
358
	private function assignCirclesToCard(DavCard $davCard) {
359
		foreach ($davCard->getGroups() as $group) {
360
			try {
361
				$davCard->addCircle(
362
					$this->circlesRequest->getFromContactGroup($davCard->getAddressBookId(), $group)
363
				);
364
			} catch (CircleDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
365
			}
366
		}
367
	}
368
369
370
	/**
371
	 * @param int $addressBookId
372
	 *
373
	 * @return Circle[]
374
	 */
375
	private function getCirclesFromBook(int $addressBookId): array {
376
		return $this->circlesRequest->getFromBook($addressBookId);
377
	}
378
379
380
	/**
381
	 * @param int $length
382
	 *
383
	 * @return string
384
	 * @deprecated
385
	 */
386
	protected function uuid(int $length = 0): string {
387
		$uuid = sprintf(
388
			'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
389
			mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
390
			mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
391
		);
392
393
		if ($length > 0) {
394
			if ($length <= 16) {
395
				$uuid = str_replace('-', '', $uuid);
396
			}
397
398
			$uuid = substr($uuid, 0, $length);
399
		}
400
401
		return $uuid;
402
	}
403
404
405
	/**
406
	 * @param int $bookId
407
	 *
408
	 * @return string
409
	 */
410
	private function getOwnerFromAddressBook(int $bookId): string {
411
		$data = $this->cardDavBackend->getAddressBookById($bookId);
412
413
		// let's assume the format is principals/users/OWNER
414
		$owner = substr($data['principaluri'], 17);
415
416
		return $owner;
417
	}
418
419
420
	/**
421
	 *
422
	 */
423
	private function migration() {
424
		$users = $this->userManager->search('');
425
		foreach ($users as $user) {
426
			$books = $this->cardDavBackend->getAddressBooksForUser('principals/users/' . $user->getUID());
427
			foreach ($books as $book) {
428
				$this->migrateBook($book['id']);
429
			}
430
		}
431
	}
432
433
434
	/**
435
	 * @param int $bookId
436
	 */
437
	private function migrateBook(int $bookId) {
438
		if (in_array($bookId, $this->migratedBooks)) {
439
			return;
440
		}
441
442
		$owner = $this->getOwnerFromAddressBook($bookId);
443
444
		foreach ($this->cardDavBackend->getCards($bookId) as $card) {
445
			$davCard = new DavCard();
446
			$davCard->setOwner($owner);
447
			$davCard->importFromDav($card['carddata']);
448
			$davCard->setAddressBookId($bookId);
449
			$davCard->setCardUri($card['uri']);
450
451
			$this->manageDavCard($davCard);
452
		}
453
454
		$this->migratedBooks[] = $bookId;
455
	}
456
457
458
}
459
460
461