Completed
Push — master ( e4992c...6d0a35 )
by
unknown
10:42
created

Calendar::childExists()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11

Duplication

Lines 1
Ratio 9.09 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 1
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Lukas Reschke <[email protected]>
5
 * @author Thomas Citharel <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 *
8
 * @copyright Copyright (c) 2018, ownCloud GmbH
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
namespace OCA\DAV\CalDAV;
25
26
use OCA\DAV\DAV\Sharing\IShareable;
27
use OCP\IL10N;
28
use Sabre\CalDAV\Backend\BackendInterface;
29
use Sabre\DAV\Exception\Forbidden;
30
use Sabre\DAV\Exception\NotFound;
31
use Sabre\DAV\PropPatch;
32
33
class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
34
	public function __construct(BackendInterface $caldavBackend, $calendarInfo, IL10N $l10n) {
35
		parent::__construct($caldavBackend, $calendarInfo);
36
37
		if ($this->getName() === BirthdayService::BIRTHDAY_CALENDAR_URI) {
38
			$this->calendarInfo['{DAV:}displayname'] = $l10n->t('Contact birthdays');
39
		}
40
	}
41
42
	/**
43
	 * Updates the list of shares.
44
	 *
45
	 * The first array is a list of people that are to be added to the
46
	 * resource.
47
	 *
48
	 * Every element in the add array has the following properties:
49
	 *   * href - A url. Usually a mailto: address
50
	 *   * commonName - Usually a first and last name, or false
51
	 *   * summary - A description of the share, can also be false
52
	 *   * readOnly - A boolean value
53
	 *
54
	 * Every element in the remove array is just the address string.
55
	 *
56
	 * @param array $add
57
	 * @param array $remove
58
	 * @return void
59
	 */
60
	public function updateShares(array $add, array $remove) {
61
		/** @var CalDavBackend $calDavBackend */
62
		$calDavBackend = $this->caldavBackend;
63
		$calDavBackend->updateShares($this, $add, $remove);
64
	}
65
66
	/**
67
	 * Returns the list of people whom this resource is shared with.
68
	 *
69
	 * Every element in this array should have the following properties:
70
	 *   * href - Often a mailto: address
71
	 *   * commonName - Optional, for example a first + last name
72
	 *   * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
73
	 *   * readOnly - boolean
74
	 *   * summary - Optional, a description for the share
75
	 *
76
	 * @return array
77
	 */
78
	public function getShares() {
79
		/** @var CalDavBackend $calDavBackend */
80
		$calDavBackend = $this->caldavBackend;
81
		return $calDavBackend->getShares($this->getResourceId());
82
	}
83
84
	/**
85
	 * @return int
86
	 */
87
	public function getResourceId() {
88
		return $this->calendarInfo['id'];
89
	}
90
91
	/**
92
	 * @return string
93
	 */
94
	public function getPrincipalURI() {
95
		return $this->calendarInfo['principaluri'];
96
	}
97
98 View Code Duplication
	public function getACL() {
99
		$acl =  [
100
			[
101
				'privilege' => '{DAV:}read',
102
				'principal' => $this->getOwner(),
103
				'protected' => true,
104
			]];
105
		if ($this->getName() !== BirthdayService::BIRTHDAY_CALENDAR_URI) {
106
			$acl[] = [
107
				'privilege' => '{DAV:}write',
108
				'principal' => $this->getOwner(),
109
				'protected' => true,
110
			];
111
		}
112
		if ($this->getOwner() !== parent::getOwner()) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getOwner() instead of getACL()). Are you sure this is correct? If so, you might want to change this to $this->getOwner().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
113
			$acl[] =  [
114
					'privilege' => '{DAV:}read',
115
					'principal' => parent::getOwner(),
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getOwner() instead of getACL()). Are you sure this is correct? If so, you might want to change this to $this->getOwner().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
116
					'protected' => true,
117
				];
118
			if ($this->canWrite()) {
119
				$acl[] = [
120
					'privilege' => '{DAV:}write',
121
					'principal' => parent::getOwner(),
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getOwner() instead of getACL()). Are you sure this is correct? If so, you might want to change this to $this->getOwner().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
122
					'protected' => true,
123
				];
124
			}
125
		}
126
		if ($this->isPublic()) {
127
			$acl[] = [
128
				'privilege' => '{DAV:}read',
129
				'principal' => 'principals/system/public',
130
				'protected' => true,
131
			];
132
		}
133
134
		/** @var CalDavBackend $calDavBackend */
135
		$calDavBackend = $this->caldavBackend;
136
		return $calDavBackend->applyShareAcl($this->getResourceId(), $acl);
137
	}
138
139
	public function getChildACL() {
140
		return $this->getACL();
141
	}
142
143
	public function getOwner() {
144
		if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
145
			return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'];
146
		}
147
		return parent::getOwner();
148
	}
149
150
	public function delete() {
151
		if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
152
			$principal = 'principal:' . parent::getOwner();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getOwner() instead of delete()). Are you sure this is correct? If so, you might want to change this to $this->getOwner().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
153
			$shares = $this->getShares();
154
			$shares = \array_filter($shares, function ($share) use ($principal) {
155
				return $share['href'] === $principal;
156
			});
157
			if (empty($shares)) {
158
				throw new Forbidden();
159
			}
160
161
			/** @var CalDavBackend $calDavBackend */
162
			$calDavBackend = $this->caldavBackend;
163
			$calDavBackend->updateShares($this, [], [
164
				$principal
165
			]);
166
			return;
167
		}
168
		parent::delete();
169
	}
170
171
	public function propPatch(PropPatch $propPatch) {
172
		$mutations = $propPatch->getMutations();
173
		// If this is a shared calendar, the user can only change the enabled property, to hide it.
174
		if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) && (\sizeof($mutations) !== 1 || !isset($mutations['{http://owncloud.org/ns}calendar-enabled']))) {
175
			throw new Forbidden();
176
		}
177
		parent::propPatch($propPatch);
178
	}
179
180
	public function getChild($name) {
181
		$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
182
183
		if (!$obj) {
184
			throw new NotFound('Calendar object not found');
185
		}
186
187
		if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
188
			throw new NotFound('Calendar object not found');
189
		}
190
191
		$obj['acl'] = $this->getChildACL();
192
193
		return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
194
	}
195
196 View Code Duplication
	public function getChildren() {
197
		$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
198
		$children = [];
199
		foreach ($objs as $obj) {
200
			if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
201
				continue;
202
			}
203
			$obj['acl'] = $this->getChildACL();
204
			$children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
205
		}
206
		return $children;
207
	}
208
209 View Code Duplication
	public function getMultipleChildren(array $paths) {
210
		$objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
211
		$children = [];
212
		foreach ($objs as $obj) {
213
			if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
214
				continue;
215
			}
216
			$obj['acl'] = $this->getChildACL();
217
			$children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
218
		}
219
		return $children;
220
	}
221
222
	public function childExists($name) {
223
		$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
224
		if (!$obj) {
225
			return false;
226
		}
227
		if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) {
228
			return false;
229
		}
230
231
		return true;
232
	}
233
234
	public function calendarQuery(array $filters) {
235
		$uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
236
		if ($this->isShared()) {
237
			return \array_filter($uris, function ($uri) {
238
				return $this->childExists($uri);
239
			});
240
		}
241
242
		return $uris;
243
	}
244
245
	/**
246
	 * @param boolean $value
247
	 * @return string|null
248
	 */
249
	public function setPublishStatus($value) {
250
		$publicUri = $this->caldavBackend->setPublishStatus($value, $this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sabre\CalDAV\Backend\BackendInterface as the method setPublishStatus() does only exist in the following implementations of said interface: OCA\DAV\CalDAV\CalDavBackend, Sabre\CalDAV\Backend\MockSharing, Sabre\CalDAV\Backend\PDO.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
251
		$this->calendarInfo['publicuri'] = $publicUri;
252
		return $publicUri;
253
	}
254
255
	/**
256
	 * @return mixed $value
257
	 */
258
	public function getPublishStatus() {
259
		return $this->caldavBackend->getPublishStatus($this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sabre\CalDAV\Backend\BackendInterface as the method getPublishStatus() does only exist in the following implementations of said interface: OCA\DAV\CalDAV\CalDavBackend.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
260
	}
261
262
	private function canWrite() {
263
		if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) {
264
			return !$this->calendarInfo['{http://owncloud.org/ns}read-only'];
265
		}
266
		return true;
267
	}
268
269
	private function isPublic() {
270
		return isset($this->calendarInfo['{http://owncloud.org/ns}public']);
271
	}
272
273
	private function isShared() {
274
		return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']);
275
	}
276
277
	public function isSubscription() {
278
		return isset($this->calendarInfo['{http://calendarserver.org/ns/}source']);
279
	}
280
}
281