Passed
Push — master ( 5079bf...73205a )
by Blizzz
18:18 queued 03:07
created

CalendarImpl::handleIMipMessage()   B

Complexity

Conditions 10
Paths 19

Size

Total Lines 50
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 30
nc 19
nop 2
dl 0
loc 50
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @copyright 2017, Georg Ehrke <[email protected]>
7
 *
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Georg Ehrke <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 *
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
namespace OCA\DAV\CalDAV;
29
30
use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin;
31
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
32
use OCP\AppFramework\Utility\ITimeFactory;
33
use OCP\Calendar\Exceptions\CalendarException;
34
use OCP\Calendar\ICreateFromString;
35
use OCP\Constants;
36
use OCP\Security\ISecureRandom;
37
use Psr\Log\LoggerInterface;
38
use Sabre\DAV\Exception\Conflict;
39
use Sabre\VObject\Component\VCalendar;
40
use Sabre\VObject\Component\VEvent;
41
use Sabre\VObject\Document;
42
use Sabre\VObject\ITip\Message;
43
use Sabre\VObject\Property\VCard\DateTime;
44
use Sabre\VObject\Reader;
45
use function Sabre\Uri\split as uriSplit;
46
47
class CalendarImpl implements ICreateFromString {
48
49
	/** @var CalDavBackend */
50
	private $backend;
51
52
	/** @var Calendar */
53
	private $calendar;
54
55
	/** @var array */
56
	private $calendarInfo;
57
58
	public function __construct(Calendar $calendar,
59
								array $calendarInfo,
60
								CalDavBackend $backend) {
61
		$this->calendar = $calendar;
62
		$this->calendarInfo = $calendarInfo;
63
		$this->backend = $backend;
64
	}
65
66
	/**
67
	 * @return string defining the technical unique key
68
	 * @since 13.0.0
69
	 */
70
	public function getKey() {
71
		return $this->calendarInfo['id'];
72
	}
73
74
	/**
75
	 * {@inheritDoc}
76
	 */
77
	public function getUri(): string {
78
		return $this->calendarInfo['uri'];
79
	}
80
81
	/**
82
	 * In comparison to getKey() this function returns a human readable (maybe translated) name
83
	 * @return null|string
84
	 * @since 13.0.0
85
	 */
86
	public function getDisplayName() {
87
		return $this->calendarInfo['{DAV:}displayname'];
88
	}
89
90
	/**
91
	 * Calendar color
92
	 * @return null|string
93
	 * @since 13.0.0
94
	 */
95
	public function getDisplayColor() {
96
		return $this->calendarInfo['{http://apple.com/ns/ical/}calendar-color'];
97
	}
98
99
	/**
100
	 * @param string $pattern which should match within the $searchProperties
101
	 * @param array $searchProperties defines the properties within the query pattern should match
102
	 * @param array $options - optional parameters:
103
	 * 	['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
104
	 * @param integer|null $limit - limit number of search results
105
	 * @param integer|null $offset - offset for paging of search results
106
	 * @return array an array of events/journals/todos which are arrays of key-value-pairs
107
	 * @since 13.0.0
108
	 */
109
	public function search($pattern, array $searchProperties = [], array $options = [], $limit = null, $offset = null) {
110
		return $this->backend->search($this->calendarInfo, $pattern,
111
			$searchProperties, $options, $limit, $offset);
112
	}
113
114
	/**
115
	 * @return integer build up using \OCP\Constants
116
	 * @since 13.0.0
117
	 */
118
	public function getPermissions() {
119
		$permissions = $this->calendar->getACL();
120
		$result = 0;
121
		foreach ($permissions as $permission) {
122
			switch ($permission['privilege']) {
123
				case '{DAV:}read':
124
					$result |= Constants::PERMISSION_READ;
125
					break;
126
				case '{DAV:}write':
127
					$result |= Constants::PERMISSION_CREATE;
128
					$result |= Constants::PERMISSION_UPDATE;
129
					break;
130
				case '{DAV:}all':
131
					$result |= Constants::PERMISSION_ALL;
132
					break;
133
			}
134
		}
135
136
		return $result;
137
	}
138
139
	/**
140
	 * Create a new calendar event for this calendar
141
	 * by way of an ICS string
142
	 *
143
	 * @param string $name the file name - needs to contain the .ics ending
144
	 * @param string $calendarData a string containing a valid VEVENT ics
145
	 *
146
	 * @throws CalendarException
147
	 */
148
	public function createFromString(string $name, string $calendarData): void {
149
		$server = new InvitationResponseServer(false);
150
151
		/** @var CustomPrincipalPlugin $plugin */
152
		$plugin = $server->server->getPlugin('auth');
153
		// we're working around the previous implementation
154
		// that only allowed the public system principal to be used
155
		// so set the custom principal here
156
		$plugin->setCurrentPrincipal($this->calendar->getPrincipalURI());
157
158
		if (empty($this->calendarInfo['uri'])) {
159
			throw new CalendarException('Could not write to calendar as URI parameter is missing');
160
		}
161
162
		// Build full calendar path
163
		[, $user] = uriSplit($this->calendar->getPrincipalURI());
164
		$fullCalendarFilename = sprintf('calendars/%s/%s/%s', $user, $this->calendarInfo['uri'], $name);
165
166
		// Force calendar change URI
167
		/** @var Schedule\Plugin $schedulingPlugin */
168
		$schedulingPlugin = $server->server->getPlugin('caldav-schedule');
169
		$schedulingPlugin->setPathOfCalendarObjectChange($fullCalendarFilename);
170
171
		$stream = fopen('php://memory', 'rb+');
172
		fwrite($stream, $calendarData);
173
		rewind($stream);
174
		try {
175
			$server->server->createFile($fullCalendarFilename, $stream);
176
		} catch (Conflict $e) {
177
			throw new CalendarException('Could not create new calendar event: ' . $e->getMessage(), 0, $e);
178
		} finally {
179
			fclose($stream);
180
		}
181
	}
182
183
	/**
184
	 * @throws CalendarException
185
	 */
186
	public function handleIMipMessage(string $name, string $calendarData): void {
187
		$server = new InvitationResponseServer(false);
188
189
		/** @var CustomPrincipalPlugin $plugin */
190
		$plugin = $server->server->getPlugin('auth');
191
		// we're working around the previous implementation
192
		// that only allowed the public system principal to be used
193
		// so set the custom principal here
194
		$plugin->setCurrentPrincipal($this->calendar->getPrincipalURI());
195
196
		if (empty($this->calendarInfo['uri'])) {
197
			throw new CalendarException('Could not write to calendar as URI parameter is missing');
198
		}
199
		// Force calendar change URI
200
		/** @var Schedule\Plugin $schedulingPlugin */
201
		$schedulingPlugin = $server->server->getPlugin('caldav-schedule');
202
		// Let sabre handle the rest
203
		$iTipMessage = new Message();
204
		/** @var VCalendar $vObject */
205
		$vObject = Reader::read($calendarData);
206
		/** @var VEvent $vEvent */
207
		$vEvent = $vObject->{'VEVENT'};
208
209
		if($vObject->{'METHOD'} === null) {
210
			throw new CalendarException('No Method provided for scheduling data. Could not process message');
211
		}
212
213
		if(!isset($vEvent->{'ORGANIZER'}) || !isset($vEvent->{'ATTENDEE'})) {
214
			throw new CalendarException('Could not process scheduling data, neccessary data missing from ICAL');
215
		}
216
		$orgaizer = $vEvent->{'ORGANIZER'}->getValue();
217
		$attendee = $vEvent->{'ATTENDEE'}->getValue();
218
219
		$iTipMessage->method = $vObject->{'METHOD'}->getValue();
220
		if($iTipMessage->method === 'REPLY') {
221
			if ($server->isExternalAttendee($vEvent->{'ATTENDEE'}->getValue())) {
222
				$iTipMessage->recipient = $orgaizer;
223
			} else {
224
				$iTipMessage->recipient = $attendee;
225
			}
226
			$iTipMessage->sender = $attendee;
227
		} else if($iTipMessage->method === 'CANCEL') {
228
			$iTipMessage->recipient = $attendee;
229
			$iTipMessage->sender = $orgaizer;
230
		}
231
		$iTipMessage->uid = isset($vEvent->{'UID'}) ? $vEvent->{'UID'}->getValue() : '';
232
		$iTipMessage->component = 'VEVENT';
233
		$iTipMessage->sequence = isset($vEvent->{'SEQUENCE'}) ? (int)$vEvent->{'SEQUENCE'}->getValue() : 0;
234
		$iTipMessage->message = $vObject;
235
		$schedulingPlugin->scheduleLocalDelivery($iTipMessage);
236
	}
237
}
238