Passed
Push — master ( 9e596d...9f70c6 )
by Christoph
15:44 queued 10s
created

Calendar::getOwner()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Gary Kim <[email protected]>
7
 * @author Georg Ehrke <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Thomas Citharel <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program. If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\DAV\CalDAV;
31
32
use OCA\DAV\DAV\Sharing\IShareable;
33
use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
34
use OCP\IConfig;
35
use OCP\IL10N;
36
use Sabre\CalDAV\Backend\BackendInterface;
37
use Sabre\DAV\Exception\Forbidden;
38
use Sabre\DAV\Exception\NotFound;
39
use Sabre\DAV\PropPatch;
40
41
/**
42
 * Class Calendar
43
 *
44
 * @package OCA\DAV\CalDAV
45
 * @property CalDavBackend $caldavBackend
46
 */
47
class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable {
48
49
	/** @var IConfig */
50
	private $config;
51
52
	/** @var IL10N */
53
	protected $l10n;
54
55
	/** @var bool */
56
	private $useTrashbin = true;
57
58
	/**
59
	 * Calendar constructor.
60
	 *
61
	 * @param BackendInterface $caldavBackend
62
	 * @param $calendarInfo
63
	 * @param IL10N $l10n
64
	 * @param IConfig $config
65
	 */
66
	public function __construct(BackendInterface $caldavBackend, $calendarInfo, IL10N $l10n, IConfig $config) {
67
		parent::__construct($caldavBackend, $calendarInfo);
68
69
		if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
70
			$this->calendarInfo['{DAV:}displayname'] = $l10n->t('Contact birthdays');
71
		}
72
		if ($this->getName() === CalDavBackend::PERSONAL_CALENDAR_URI &&
73
			$this->calendarInfo['{DAV:}displayname'] === CalDavBackend::PERSONAL_CALENDAR_NAME) {
74
			$this->calendarInfo['{DAV:}displayname'] = $l10n->t('Personal');
75
		}
76
77
		$this->config = $config;
78
		$this->l10n = $l10n;
79
	}
80
81
	/**
82
	 * Updates the list of shares.
83
	 *
84
	 * The first array is a list of people that are to be added to the
85
	 * resource.
86
	 *
87
	 * Every element in the add array has the following properties:
88
	 *   * href - A url. Usually a mailto: address
89
	 *   * commonName - Usually a first and last name, or false
90
	 *   * summary - A description of the share, can also be false
91
	 *   * readOnly - A boolean value
92
	 *
93
	 * Every element in the remove array is just the address string.
94
	 *
95
	 * @param array $add
96
	 * @param array $remove
97
	 * @return void
98
	 * @throws Forbidden
99
	 */
100
	public function updateShares(array $add, array $remove) {
101
		if ($this->isShared()) {
102
			throw new Forbidden();
103
		}
104
		$this->caldavBackend->updateShares($this, $add, $remove);
105
	}
106
107
	/**
108
	 * Returns the list of people whom this resource is shared with.
109
	 *
110
	 * Every element in this array should have the following properties:
111
	 *   * href - Often a mailto: address
112
	 *   * commonName - Optional, for example a first + last name
113
	 *   * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
114
	 *   * readOnly - boolean
115
	 *   * summary - Optional, a description for the share
116
	 *
117
	 * @return array
118
	 */
119
	public function getShares() {
120
		if ($this->isShared()) {
121
			return [];
122
		}
123
		return $this->caldavBackend->getShares($this->getResourceId());
124
	}
125
126
	/**
127
	 * @return int
128
	 */
129
	public function getResourceId() {
130
		return $this->calendarInfo['id'];
131
	}
132
133
	/**
134
	 * @return string
135
	 */
136
	public function getPrincipalURI() {
137
		return $this->calendarInfo['principaluri'];
138
	}
139
140
	/**
141
	 * @return array
142
	 */
143
	public function getACL() {
144
		$acl = [
145
			[
146
				'privilege' => '{DAV:}read',
147
				'principal' => $this->getOwner(),
148
				'protected' => true,
149
			],
150
			[
151
				'privilege' => '{DAV:}read',
152
				'principal' => $this->getOwner() . '/calendar-proxy-write',
153
				'protected' => true,
154
			],
155
			[
156
				'privilege' => '{DAV:}read',
157
				'principal' => $this->getOwner() . '/calendar-proxy-read',
158
				'protected' => true,
159
			],
160
		];
161
162
		if ($this->getName() !== BirthdayService::BIRTHDAY_CALENDAR_URI) {
163
			$acl[] = [
164
				'privilege' => '{DAV:}write',
165
				'principal' => $this->getOwner(),
166
				'protected' => true,
167
			];
168
			$acl[] = [
169
				'privilege' => '{DAV:}write',
170
				'principal' => $this->getOwner() . '/calendar-proxy-write',
171
				'protected' => true,
172
			];
173
		} else {
174
			$acl[] = [
175
				'privilege' => '{DAV:}write-properties',
176
				'principal' => $this->getOwner(),
177
				'protected' => true,
178
			];
179
			$acl[] = [
180
				'privilege' => '{DAV:}write-properties',
181
				'principal' => $this->getOwner() . '/calendar-proxy-write',
182
				'protected' => true,
183
			];
184
		}
185
186
		$acl[] = [
187
			'privilege' => '{DAV:}write-properties',
188
			'principal' => $this->getOwner() . '/calendar-proxy-read',
189
			'protected' => true,
190
		];
191
192
		if (!$this->isShared()) {
193
			return $acl;
194
		}
195
196
		if ($this->getOwner() !== parent::getOwner()) {
197
			$acl[] = [
198
				'privilege' => '{DAV:}read',
199
				'principal' => parent::getOwner(),
200
				'protected' => true,
201
			];
202
			if ($this->canWrite()) {
203
				$acl[] = [
204
					'privilege' => '{DAV:}write',
205
					'principal' => parent::getOwner(),
206
					'protected' => true,
207
				];
208
			} else {
209
				$acl[] = [
210
					'privilege' => '{DAV:}write-properties',
211
					'principal' => parent::getOwner(),
212
					'protected' => true,
213
				];
214
			}
215
		}
216
		if ($this->isPublic()) {
217
			$acl[] = [
218
				'privilege' => '{DAV:}read',
219
				'principal' => 'principals/system/public',
220
				'protected' => true,
221
			];
222
		}
223
224
		$acl = $this->caldavBackend->applyShareAcl($this->getResourceId(), $acl);
225
		$allowedPrincipals = [
226
			$this->getOwner(),
227
			$this->getOwner(). '/calendar-proxy-read',
228
			$this->getOwner(). '/calendar-proxy-write',
229
			parent::getOwner(),
230
			'principals/system/public'
231
		];
232
		return array_filter($acl, function ($rule) use ($allowedPrincipals) {
233
			return \in_array($rule['principal'], $allowedPrincipals, true);
234
		});
235
	}
236
237
	public function getChildACL() {
238
		return $this->getACL();
239
	}
240
241
	public function getOwner() {
242
		if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
243
			return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'];
244
		}
245
		return parent::getOwner();
246
	}
247
248
	public function delete() {
249
		if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) &&
250
			$this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']) {
251
			$principal = 'principal:' . parent::getOwner();
252
			$shares = $this->caldavBackend->getShares($this->getResourceId());
253
			$shares = array_filter($shares, function ($share) use ($principal) {
254
				return $share['href'] === $principal;
255
			});
256
			if (empty($shares)) {
257
				throw new Forbidden();
258
			}
259
260
			$this->caldavBackend->updateShares($this, [], [
261
				$principal
262
			]);
263
			return;
264
		}
265
266
		// Remember when a user deleted their birthday calendar
267
		// in order to not regenerate it on the next contacts change
268
		if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
269
			$principalURI = $this->getPrincipalURI();
270
			$userId = substr($principalURI, 17);
271
272
			$this->config->setUserValue($userId, 'dav', 'generateBirthdayCalendar', 'no');
273
		}
274
275
		$this->caldavBackend->deleteCalendar(
276
			$this->calendarInfo['id'],
277
			!$this->useTrashbin
278
		);
279
	}
280
281
	public function propPatch(PropPatch $propPatch) {
282
		// parent::propPatch will only update calendars table
283
		// if calendar is shared, changes have to be made to the properties table
284
		if (!$this->isShared()) {
285
			parent::propPatch($propPatch);
286
		}
287
	}
288
289
	public function getChild($name) {
290
		$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
291
292
		if (!$obj) {
293
			throw new NotFound('Calendar object not found');
294
		}
295
296
		if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
297
			throw new NotFound('Calendar object not found');
298
		}
299
300
		$obj['acl'] = $this->getChildACL();
301
302
		return new CalendarObject($this->caldavBackend, $this->l10n, $this->calendarInfo, $obj);
303
	}
304
305
	public function getChildren() {
306
		$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
307
		$children = [];
308
		foreach ($objs as $obj) {
309
			if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
310
				continue;
311
			}
312
			$obj['acl'] = $this->getChildACL();
313
			$children[] = new CalendarObject($this->caldavBackend, $this->l10n, $this->calendarInfo, $obj);
314
		}
315
		return $children;
316
	}
317
318
	public function getMultipleChildren(array $paths) {
319
		$objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
320
		$children = [];
321
		foreach ($objs as $obj) {
322
			if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
323
				continue;
324
			}
325
			$obj['acl'] = $this->getChildACL();
326
			$children[] = new CalendarObject($this->caldavBackend, $this->l10n, $this->calendarInfo, $obj);
327
		}
328
		return $children;
329
	}
330
331
	public function childExists($name) {
332
		$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
333
		if (!$obj) {
334
			return false;
335
		}
336
		if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) {
337
			return false;
338
		}
339
340
		return true;
341
	}
342
343
	public function calendarQuery(array $filters) {
344
		$uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
345
		if ($this->isShared()) {
346
			return array_filter($uris, function ($uri) {
347
				return $this->childExists($uri);
348
			});
349
		}
350
351
		return $uris;
352
	}
353
354
	/**
355
	 * @param boolean $value
356
	 * @return string|null
357
	 */
358
	public function setPublishStatus($value) {
359
		$publicUri = $this->caldavBackend->setPublishStatus($value, $this);
360
		$this->calendarInfo['publicuri'] = $publicUri;
361
		return $publicUri;
362
	}
363
364
	/**
365
	 * @return mixed $value
366
	 */
367
	public function getPublishStatus() {
368
		return $this->caldavBackend->getPublishStatus($this);
369
	}
370
371
	public function canWrite() {
372
		if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
373
			return false;
374
		}
375
376
		if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) {
377
			return !$this->calendarInfo['{http://owncloud.org/ns}read-only'];
378
		}
379
		return true;
380
	}
381
382
	private function isPublic() {
383
		return isset($this->calendarInfo['{http://owncloud.org/ns}public']);
384
	}
385
386
	protected function isShared() {
387
		if (!isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
388
			return false;
389
		}
390
391
		return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri'];
392
	}
393
394
	public function isSubscription() {
395
		return isset($this->calendarInfo['{http://calendarserver.org/ns/}source']);
396
	}
397
398
	/**
399
	 * @inheritDoc
400
	 */
401
	public function getChanges($syncToken, $syncLevel, $limit = null) {
402
		if (!$syncToken && $limit) {
403
			throw new UnsupportedLimitOnInitialSyncException();
404
		}
405
406
		return parent::getChanges($syncToken, $syncLevel, $limit);
407
	}
408
409
	public function restore(): void {
410
		$this->caldavBackend->restoreCalendar((int) $this->calendarInfo['id']);
411
	}
412
413
	public function disableTrashbin(): void {
414
		$this->useTrashbin = false;
415
	}
416
}
417