Completed
Pull Request — master (#347)
by Maxence
02:00
created

DavService::uuid()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
cc 3
nc 3
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
	/**
80
	 * TimezoneService constructor.
81
	 *
82
	 * @param string $userId
83
	 * @param IUserManager $userManager
84
	 * @param CirclesRequest $circlesRequest
85
	 * @param MembersRequest $membersRequest
86
	 * @param ConfigService $configService
87
	 * @param MiscService $miscService
88
	 */
89 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...
90
		$userId, IUserManager $userManager, CardDavBackend $cardDavBackend, CirclesRequest $circlesRequest,
91
		MembersRequest $membersRequest, ConfigService $configService, MiscService $miscService
92
	) {
93
		$this->userId = $userId;
94
		$this->userManager = $userManager;
95
		$this->cardDavBackend = $cardDavBackend;
96
		$this->circlesRequest = $circlesRequest;
97
		$this->membersRequest = $membersRequest;
98
		$this->configService = $configService;
99
		$this->miscService = $miscService;
100
	}
101
102
103
	/**
104
	 * @param ManagerEvent $event
105
	 */
106
	public function onAppEnabled(ManagerEvent $event) {
107
		if ($event->getAppID() !== 'circles') {
108
			return;
109
		}
110
111
		$this->migration();
112
	}
113
114
115
	/**
116
	 * @param GenericEvent $event
117
	 */
118
	public function onCreateCard(GenericEvent $event) {
119
		$davCard = $this->generateDavCard($event);
120
		$this->updateDavCard($davCard);
121
	}
122
123
124
	/**
125
	 * @param GenericEvent $event
126
	 */
127
	public function onUpdateCard(GenericEvent $event) {
128
		$davCard = $this->generateDavCard($event);
129
		$this->updateDavCard($davCard);
130
	}
131
132
133
	/**
134
	 * @param GenericEvent $event
135
	 */
136
	public function onDeleteCard(GenericEvent $event) {
137
		$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...
138
		$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...
139
	}
140
141
142
	/**
143
	 * @param GenericEvent $event
144
	 *
145
	 * @return DavCard
146
	 */
147
	private function generateDavCard(GenericEvent $event): DavCard {
148
		$addressBookId = $event->getArgument('addressBookId');
149
		$cardUri = $event->getArgument('cardUri');
150
		$cardData = $event->getArgument('cardData');
151
152
		$davCard = new DavCard();
153
		$davCard->setOwner($this->getOwnerFromAddressBook($addressBookId));
154
		$davCard->importFromDav($cardData);
155
		$davCard->setAddressBookId($addressBookId);
156
		$davCard->setCardUri($cardUri);
157
158
		return $davCard;
159
	}
160
161
162
	/**
163
	 * @param DavCard $davCard
164
	 */
165
	private function updateDavCard(DavCard $davCard) {
166
		$this->miscService->log('Updating Card: ' . json_encode($davCard));
167
		$this->manageCircles($davCard);
168
		$this->manageContact($davCard);
169
	}
170
171
172
	/**
173
	 * @param DavCard $davCard
174
	 */
175
	private function manageContact(DavCard $davCard) {
176
		$circles = array_map(
177
			function(Circle $circle) {
178
				return $circle->getUniqueId();
179
			}, $davCard->getCircles()
180
		);
181
182
		try {
183
			$userId = $this->isLocalMember($davCard);
184
185
			$this->manageLocalContact($circles, $davCard, $userId);
186
		} catch (NotLocalMemberException $e) {
187
			$this->manageRemoteContact($circles, $davCard);
188
		}
189
	}
190
191
192
	/**
193
	 * @param array $circles
194
	 * @param DavCard $davCard
195
	 * @param string $userId
196
	 */
197
	private function manageLocalContact(array $circles, DavCard $davCard, string $userId) {
0 ignored issues
show
Unused Code introduced by
The parameter $circles 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...
198
		foreach ($davCard->getCircles() as $circle) {
199
			$this->manageMember($circle, $davCard, Member::TYPE_USER, $userId);
200
		}
201
202
	}
203
204
205
	/**
206
	 * @param array $circles
207
	 * @param DavCard $davCard
208
	 */
209
	private function manageRemoteContact(array $circles, DavCard $davCard) {
0 ignored issues
show
Unused Code introduced by
The parameter $circles 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...
210
		foreach ($davCard->getCircles() as $circle) {
211
			$this->manageMember($circle, $davCard, Member::TYPE_CONTACT);
212
		}
213
	}
214
215
216
	/**
217
	 * @param Circle $circle
218
	 * @param DavCard $davCard
219
	 * @param int $type
220
	 * @param string $userId
221
	 */
222
	private function manageMember(Circle $circle, DavCard $davCard, int $type, string $userId = '') {
223
		if ($userId === '') {
224
			$userId = $davCard->getOwner() . ':' . $davCard->getContactId();
225
		}
226
227
		try {
228
			$member =
229
				$this->membersRequest->getContactMember($circle->getUniqueId(), $davCard->getContactId());
230
231
			if ($member->getType() !== $type) {
232
				$this->membersRequest->removeMember($member);
233
				throw new MemberDoesNotExistException();
234
			}
235
		} catch (MemberDoesNotExistException $e) {
236
			$member = new Member();
237
			$member->setLevel(Member::LEVEL_MEMBER);
238
			$member->setStatus(Member::STATUS_MEMBER);
239
			$member->setContactId($davCard->getContactId());
240
			$member->setType($type);
241
			$member->setCircleId($circle->getUniqueId());
242
			$member->setUserId($userId);
243
244
			try {
245
				$this->membersRequest->createMember($member);
246
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
247
			}
248
		}
249
	}
250
251
252
	/**
253
	 * @param DavCard $davCard
254
	 *
255
	 * @return string
256
	 * @throws NotLocalMemberException
257
	 */
258
	private function isLocalMember(DavCard $davCard): string {
259
		foreach ($davCard->getEmails() as $email) {
260
			if (strpos($email, '@') === false) {
261
				continue;
262
			}
263
264
			list ($username, $domain) = explode('@', $email);
265
			if (in_array($domain, $this->configService->getAvailableHosts())) {
266
				$user = $this->userManager->get($username);
267
				if ($user !== null) {
268
					return $user->getUID();
269
				}
270
			}
271
		}
272
273
		throw new NotLocalMemberException();
274
	}
275
276
277
	/**
278
	 * @param DavCard $davCard
279
	 */
280
	private function manageCircles(DavCard $davCard) {
281
		$fromCard = $davCard->getGroups();
282
		$current = array_map(
283
			function(Circle $circle) {
284
				return $circle->getContactGroupName();
285
			}, $this->getCirclesFromBook($davCard->getAddressBookId())
286
		);
287
288
		$this->manageNewCircles($davCard, $fromCard, $current);
289
		$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...
290
291
		$this->assignCirclesToCard($davCard);
292
	}
293
294
295
	/**
296
	 * @param DavCard $davCard
297
	 * @param array $fromCard
298
	 * @param array $current
299
	 */
300
	private function manageNewCircles(DavCard $davCard, array $fromCard, array $current) {
301
		foreach ($fromCard as $group) {
302
			if (in_array($group, $current)) {
303
				continue;
304
			}
305
306
			$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...
307
			$circle->setContactAddressBook($davCard->getAddressBookId());
308
			$circle->setContactGroupName($group);
309
310
			try {
311
				$this->circlesRequest->createCircle($circle, $davCard->getOwner());
312
				$member = new Member($davCard->getOwner(), Member::TYPE_USER, $circle->getUniqueId());
313
				$member->setLevel(Member::LEVEL_OWNER);
314
				$member->setStatus(Member::STATUS_MEMBER);
315
316
				try {
317
					$this->membersRequest->createMember($member);
318
				} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
319
				}
320
			} catch (CircleAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
321
			}
322
323
		}
324
	}
325
326
327
	/**
328
	 * // TODO: Get all group from an addressbook
329
	 * // TODO: remove deprecated circles
330
	 *
331
	 * @param array $fromCard
332
	 * @param array $current
333
	 */
334
	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...
335
	}
336
337
338
	/**
339
	 * @param DavCard $davCard
340
	 */
341
	private function assignCirclesToCard(DavCard $davCard) {
342
		foreach ($davCard->getGroups() as $group) {
343
			try {
344
				$davCard->addCircle(
345
					$this->circlesRequest->getFromContactGroup($davCard->getAddressBookId(), $group)
346
				);
347
			} catch (CircleDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
348
			}
349
		}
350
	}
351
352
353
	/**
354
	 * @param int $addressBookId
355
	 *
356
	 * @return Circle[]
357
	 */
358
	private function getCirclesFromBook(int $addressBookId): array {
359
		return $this->circlesRequest->getFromBook($addressBookId);
360
	}
361
362
363
	/**
364
	 * @param int $length
365
	 *
366
	 * @return string
367
	 * @deprecated
368
	 */
369
	protected function uuid(int $length = 0): string {
370
		$uuid = sprintf(
371
			'%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff),
372
			mt_rand(0, 0xffff), mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
373
			mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
374
		);
375
376
		if ($length > 0) {
377
			if ($length <= 16) {
378
				$uuid = str_replace('-', '', $uuid);
379
			}
380
381
			$uuid = substr($uuid, 0, $length);
382
		}
383
384
		return $uuid;
385
	}
386
387
388
	/**
389
	 * @param int $bookId
390
	 *
391
	 * @return string
392
	 */
393
	private function getOwnerFromAddressBook(int $bookId): string {
394
		$data = $this->cardDavBackend->getAddressBookById($bookId);
395
396
		// let's assume the format is principals/users/OWNER
397
		$owner = substr($data['principaluri'], 17);
398
399
		return $owner;
400
	}
401
402
403
	/**
404
	 *
405
	 */
406
	private function migration() {
407
		$this->miscService->log('-- TODO: init migration/sync Contacts->Circles');
408
	}
409
410
}
411
412
413