|
14 | 14 | use OCP\Calendar\CalendarExportOptions;
|
15 | 15 | use OCP\Calendar\Exceptions\CalendarException;
|
16 | 16 | use OCP\Calendar\ICalendarExport;
|
| 17 | +use OCP\Calendar\ICalendarHandleImip; |
17 | 18 | use OCP\Calendar\ICalendarIsEnabled;
|
18 | 19 | use OCP\Calendar\ICalendarIsShared;
|
19 | 20 | use OCP\Calendar\ICalendarIsWritable;
|
|
30 | 31 | use Sabre\VObject\Reader;
|
31 | 32 | use function Sabre\Uri\split as uriSplit;
|
32 | 33 |
|
33 |
| -class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIsWritable, ICalendarIsShared, ICalendarExport, ICalendarIsEnabled { |
| 34 | +class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIsEnabled, ICalendarIsWritable, ICalendarIsShared, ICalendarHandleImip, ICalendarExport { |
34 | 35 | public function __construct(
|
35 | 36 | private Calendar $calendar,
|
36 | 37 | /** @var array<string, mixed> */
|
@@ -211,60 +212,95 @@ public function createFromString(string $name, string $calendarData): void {
|
211 | 212 |
|
212 | 213 | /**
|
213 | 214 | * @throws CalendarException
|
| 215 | + * |
| 216 | + * @deprecated 32.0.0 Use handleIMip() instead |
214 | 217 | */
|
215 | 218 | public function handleIMipMessage(string $name, string $calendarData): void {
|
216 |
| - $server = $this->getInvitationResponseServer(); |
| 219 | + /** @var VCalendar $vObject */ |
| 220 | + $vObject = Reader::read($calendarData); |
| 221 | + $this->handleIMip($vObject); |
| 222 | + } |
217 | 223 |
|
218 |
| - /** @var CustomPrincipalPlugin $plugin */ |
219 |
| - $plugin = $server->getServer()->getPlugin('auth'); |
220 |
| - // we're working around the previous implementation |
221 |
| - // that only allowed the public system principal to be used |
222 |
| - // so set the custom principal here |
223 |
| - $plugin->setCurrentPrincipal($this->calendar->getPrincipalURI()); |
| 224 | + /** |
| 225 | + * Processes an iMip message |
| 226 | + * |
| 227 | + * @since 32.0.0 |
| 228 | + * |
| 229 | + * @throws CalendarException |
| 230 | + */ |
| 231 | + public function handleIMip(VCalendar $vObject): void { |
224 | 232 |
|
| 233 | + // validate the iMip message |
| 234 | + if (!isset($vObject->METHOD)) { |
| 235 | + throw new CalendarException('iMip message contains no valid method'); |
| 236 | + } |
| 237 | + if (!isset($vObject->VEVENT)) { |
| 238 | + throw new CalendarException('iMip message contains no event'); |
| 239 | + } |
| 240 | + if (!isset($vObject->VEVENT->UID)) { |
| 241 | + throw new CalendarException('iMip message event dose not contain a UID'); |
| 242 | + } |
| 243 | + if (!isset($vObject->VEVENT->ORGANIZER)) { |
| 244 | + throw new CalendarException('iMip message event dose not contain an organizer'); |
| 245 | + } |
| 246 | + if (!isset($vObject->VEVENT->ATTENDEE)) { |
| 247 | + throw new CalendarException('iMip message event dose not contain an attendee'); |
| 248 | + } |
225 | 249 | if (empty($this->calendarInfo['uri'])) {
|
226 | 250 | throw new CalendarException('Could not write to calendar as URI parameter is missing');
|
227 | 251 | }
|
| 252 | + // construct dav server |
| 253 | + $server = $this->getInvitationResponseServer(); |
| 254 | + /** @var CustomPrincipalPlugin $authPlugin */ |
| 255 | + $authPlugin = $server->getServer()->getPlugin('auth'); |
| 256 | + // we're working around the previous implementation |
| 257 | + // that only allowed the public system principal to be used |
| 258 | + // so set the custom principal here |
| 259 | + $authPlugin->setCurrentPrincipal($this->calendar->getPrincipalURI()); |
228 | 260 | // Force calendar change URI
|
229 |
| - /** @var Schedule\Plugin $schedulingPlugin */ |
| 261 | + /** @var \OCA\DAV\CalDAV\Schedule\Plugin $schedulingPlugin */ |
230 | 262 | $schedulingPlugin = $server->getServer()->getPlugin('caldav-schedule');
|
231 |
| - // Let sabre handle the rest |
232 |
| - $iTipMessage = new Message(); |
233 |
| - /** @var VCalendar $vObject */ |
234 |
| - $vObject = Reader::read($calendarData); |
235 |
| - /** @var VEvent $vEvent */ |
236 |
| - $vEvent = $vObject->{'VEVENT'}; |
237 |
| - |
238 |
| - if ($vObject->{'METHOD'} === null) { |
239 |
| - throw new CalendarException('No Method provided for scheduling data. Could not process message'); |
240 |
| - } |
241 |
| - |
242 |
| - if (!isset($vEvent->{'ORGANIZER'}) || !isset($vEvent->{'ATTENDEE'})) { |
243 |
| - throw new CalendarException('Could not process scheduling data, neccessary data missing from ICAL'); |
244 |
| - } |
245 |
| - $organizer = $vEvent->{'ORGANIZER'}->getValue(); |
246 |
| - $attendee = $vEvent->{'ATTENDEE'}->getValue(); |
247 |
| - |
248 |
| - $iTipMessage->method = $vObject->{'METHOD'}->getValue(); |
249 |
| - if ($iTipMessage->method === 'REQUEST') { |
250 |
| - $iTipMessage->sender = $organizer; |
251 |
| - $iTipMessage->recipient = $attendee; |
252 |
| - } elseif ($iTipMessage->method === 'REPLY') { |
253 |
| - if ($server->isExternalAttendee($vEvent->{'ATTENDEE'}->getValue())) { |
254 |
| - $iTipMessage->recipient = $organizer; |
255 |
| - } else { |
256 |
| - $iTipMessage->recipient = $attendee; |
| 263 | + // retrieve all uses addresses |
| 264 | + $userAddresses = $schedulingPlugin->getAddressesForPrincipal($this->calendar->getPrincipalURI()); |
| 265 | + $userAddresses = array_map('strtolower', $userAddresses); |
| 266 | + // validate the method, recipient and sender |
| 267 | + $imipMethod = strtoupper($vObject->METHOD->getValue()); |
| 268 | + if (in_array($imipMethod, ['REPLY', 'REFRESH'], true)) { |
| 269 | + // extract sender (REPLY and REFRESH method should only have one attendee) |
| 270 | + $sender = strtolower($vObject->VEVENT->ATTENDEE->getValue()); |
| 271 | + // extract and verify the recipient |
| 272 | + $recipient = strtolower($vObject->VEVENT->ORGANIZER->getValue()); |
| 273 | + if (!in_array($recipient, $userAddresses, true)) { |
| 274 | + throw new CalendarException('iMip message dose not contain an organizer that matches the user'); |
| 275 | + } |
| 276 | + } elseif (in_array($imipMethod, ['PUBLISH', 'REQUEST', 'ADD', 'CANCEL'], true)) { |
| 277 | + // extract sender |
| 278 | + $sender = strtolower($vObject->VEVENT->ORGANIZER->getValue()); |
| 279 | + // extract and verify the recipient |
| 280 | + foreach ($vObject->VEVENT->ATTENDEE as $attendee) { |
| 281 | + $recipient = strtolower($attendee->getValue()); |
| 282 | + if (in_array($recipient, $userAddresses, true)) { |
| 283 | + break; |
| 284 | + } |
| 285 | + $recipient = null; |
| 286 | + } |
| 287 | + if ($recipient === null) { |
| 288 | + throw new CalendarException('iMip message dose not contain an attendee that matches the user'); |
257 | 289 | }
|
258 |
| - $iTipMessage->sender = $attendee; |
259 |
| - } elseif ($iTipMessage->method === 'CANCEL') { |
260 |
| - $iTipMessage->recipient = $attendee; |
261 |
| - $iTipMessage->sender = $organizer; |
| 290 | + } else { |
| 291 | + throw new CalendarException('iMip message contains a method that is not supported: ' . $imipMethod); |
262 | 292 | }
|
263 |
| - $iTipMessage->uid = isset($vEvent->{'UID'}) ? $vEvent->{'UID'}->getValue() : ''; |
264 |
| - $iTipMessage->component = 'VEVENT'; |
265 |
| - $iTipMessage->sequence = isset($vEvent->{'SEQUENCE'}) ? (int)$vEvent->{'SEQUENCE'}->getValue() : 0; |
266 |
| - $iTipMessage->message = $vObject; |
267 |
| - $server->server->emit('schedule', [$iTipMessage]); |
| 293 | + // generate the iTip message |
| 294 | + $iTip = new Message(); |
| 295 | + $iTip->method = $imipMethod; |
| 296 | + $iTip->sender = $sender; |
| 297 | + $iTip->recipient = $recipient; |
| 298 | + $iTip->component = 'VEVENT'; |
| 299 | + $iTip->uid = $vObject->VEVENT->UID->getValue(); |
| 300 | + $iTip->sequence = (int)$vObject->VEVENT->SEQUENCE->getValue(); |
| 301 | + $iTip->message = $vObject; |
| 302 | + |
| 303 | + $server->server->emit('schedule', [$iTip]); |
268 | 304 | }
|
269 | 305 |
|
270 | 306 | public function getInvitationResponseServer(): InvitationResponseServer {
|
|
0 commit comments