Completed
Push — master ( b9da94...8c68f1 )
by Christoph
32:26 queued 14s
created
lib/private/Calendar/Manager.php 2 patches
Indentation   +576 added lines, -576 removed lines patch added patch discarded remove patch
@@ -41,581 +41,581 @@
 block discarded – undo
41 41
 use function array_merge;
42 42
 
43 43
 class Manager implements IManager {
44
-	/**
45
-	 * @var ICalendar[] holds all registered calendars
46
-	 */
47
-	private array $calendars = [];
48
-
49
-	/**
50
-	 * @var \Closure[] to call to load/register calendar providers
51
-	 */
52
-	private array $calendarLoaders = [];
53
-
54
-	public function __construct(
55
-		private Coordinator $coordinator,
56
-		private ContainerInterface $container,
57
-		private LoggerInterface $logger,
58
-		private ITimeFactory $timeFactory,
59
-		private ISecureRandom $random,
60
-		private IUserManager $userManager,
61
-		private ServerFactory $serverFactory,
62
-	) {
63
-	}
64
-
65
-	/**
66
-	 * This function is used to search and find objects within the user's calendars.
67
-	 * In case $pattern is empty all events/journals/todos will be returned.
68
-	 *
69
-	 * @param string $pattern which should match within the $searchProperties
70
-	 * @param array $searchProperties defines the properties within the query pattern should match
71
-	 * @param array $options - optional parameters:
72
-	 *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
73
-	 * @param integer|null $limit - limit number of search results
74
-	 * @param integer|null $offset - offset for paging of search results
75
-	 * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
76
-	 * @since 13.0.0
77
-	 */
78
-	public function search(
79
-		$pattern,
80
-		array $searchProperties = [],
81
-		array $options = [],
82
-		$limit = null,
83
-		$offset = null,
84
-	): array {
85
-		$this->loadCalendars();
86
-		$result = [];
87
-		foreach ($this->calendars as $calendar) {
88
-			$r = $calendar->search($pattern, $searchProperties, $options, $limit, $offset);
89
-			foreach ($r as $o) {
90
-				$o['calendar-key'] = $calendar->getKey();
91
-				$result[] = $o;
92
-			}
93
-		}
94
-
95
-		return $result;
96
-	}
97
-
98
-	/**
99
-	 * Check if calendars are available
100
-	 *
101
-	 * @return bool true if enabled, false if not
102
-	 * @since 13.0.0
103
-	 */
104
-	public function isEnabled(): bool {
105
-		return !empty($this->calendars) || !empty($this->calendarLoaders);
106
-	}
107
-
108
-	/**
109
-	 * Registers a calendar
110
-	 *
111
-	 * @since 13.0.0
112
-	 */
113
-	public function registerCalendar(ICalendar $calendar): void {
114
-		$this->calendars[$calendar->getKey()] = $calendar;
115
-	}
116
-
117
-	/**
118
-	 * Unregisters a calendar
119
-	 *
120
-	 * @since 13.0.0
121
-	 */
122
-	public function unregisterCalendar(ICalendar $calendar): void {
123
-		unset($this->calendars[$calendar->getKey()]);
124
-	}
125
-
126
-	/**
127
-	 * In order to improve lazy loading a closure can be registered which will be called in case
128
-	 * calendars are actually requested
129
-	 *
130
-	 * @since 13.0.0
131
-	 */
132
-	public function register(\Closure $callable): void {
133
-		$this->calendarLoaders[] = $callable;
134
-	}
135
-
136
-	/**
137
-	 * @return ICalendar[]
138
-	 *
139
-	 * @since 13.0.0
140
-	 */
141
-	public function getCalendars(): array {
142
-		$this->loadCalendars();
143
-
144
-		return array_values($this->calendars);
145
-	}
146
-
147
-	/**
148
-	 * removes all registered calendar instances
149
-	 *
150
-	 * @since 13.0.0
151
-	 */
152
-	public function clear(): void {
153
-		$this->calendars = [];
154
-		$this->calendarLoaders = [];
155
-	}
156
-
157
-	/**
158
-	 * loads all calendars
159
-	 */
160
-	private function loadCalendars(): void {
161
-		foreach ($this->calendarLoaders as $callable) {
162
-			$callable($this);
163
-		}
164
-		$this->calendarLoaders = [];
165
-	}
166
-
167
-	/**
168
-	 * @return ICreateFromString[]
169
-	 */
170
-	public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array {
171
-		$context = $this->coordinator->getRegistrationContext();
172
-		if ($context === null) {
173
-			return [];
174
-		}
175
-
176
-		return array_merge(
177
-			...array_map(function ($registration) use ($principalUri, $calendarUris) {
178
-				try {
179
-					/** @var ICalendarProvider $provider */
180
-					$provider = $this->container->get($registration->getService());
181
-				} catch (Throwable $e) {
182
-					$this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
183
-						'exception' => $e,
184
-					]);
185
-					return [];
186
-				}
187
-
188
-				return $provider->getCalendars($principalUri, $calendarUris);
189
-			}, $context->getCalendarProviders())
190
-		);
191
-	}
192
-
193
-	public function searchForPrincipal(ICalendarQuery $query): array {
194
-		/** @var CalendarQuery $query */
195
-		$calendars = $this->getCalendarsForPrincipal(
196
-			$query->getPrincipalUri(),
197
-			$query->getCalendarUris(),
198
-		);
199
-
200
-		$results = [];
201
-		foreach ($calendars as $calendar) {
202
-			$r = $calendar->search(
203
-				$query->getSearchPattern() ?? '',
204
-				$query->getSearchProperties(),
205
-				$query->getOptions(),
206
-				$query->getLimit(),
207
-				$query->getOffset()
208
-			);
209
-
210
-			foreach ($r as $o) {
211
-				$o['calendar-key'] = $calendar->getKey();
212
-				$o['calendar-uri'] = $calendar->getUri();
213
-				$results[] = $o;
214
-			}
215
-		}
216
-		return $results;
217
-	}
218
-
219
-	public function newQuery(string $principalUri): ICalendarQuery {
220
-		return new CalendarQuery($principalUri);
221
-	}
222
-
223
-	/**
224
-	 * @since 31.0.0
225
-	 * @throws \OCP\DB\Exception
226
-	 */
227
-	public function handleIMipRequest(
228
-		string $principalUri,
229
-		string $sender,
230
-		string $recipient,
231
-		string $calendarData,
232
-	): bool {
233
-
234
-		$userCalendars = $this->getCalendarsForPrincipal($principalUri);
235
-		if (empty($userCalendars)) {
236
-			$this->logger->warning('iMip message could not be processed because user has no calendars');
237
-			return false;
238
-		}
44
+    /**
45
+     * @var ICalendar[] holds all registered calendars
46
+     */
47
+    private array $calendars = [];
48
+
49
+    /**
50
+     * @var \Closure[] to call to load/register calendar providers
51
+     */
52
+    private array $calendarLoaders = [];
53
+
54
+    public function __construct(
55
+        private Coordinator $coordinator,
56
+        private ContainerInterface $container,
57
+        private LoggerInterface $logger,
58
+        private ITimeFactory $timeFactory,
59
+        private ISecureRandom $random,
60
+        private IUserManager $userManager,
61
+        private ServerFactory $serverFactory,
62
+    ) {
63
+    }
64
+
65
+    /**
66
+     * This function is used to search and find objects within the user's calendars.
67
+     * In case $pattern is empty all events/journals/todos will be returned.
68
+     *
69
+     * @param string $pattern which should match within the $searchProperties
70
+     * @param array $searchProperties defines the properties within the query pattern should match
71
+     * @param array $options - optional parameters:
72
+     *                       ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
73
+     * @param integer|null $limit - limit number of search results
74
+     * @param integer|null $offset - offset for paging of search results
75
+     * @return array an array of events/journals/todos which are arrays of arrays of key-value-pairs
76
+     * @since 13.0.0
77
+     */
78
+    public function search(
79
+        $pattern,
80
+        array $searchProperties = [],
81
+        array $options = [],
82
+        $limit = null,
83
+        $offset = null,
84
+    ): array {
85
+        $this->loadCalendars();
86
+        $result = [];
87
+        foreach ($this->calendars as $calendar) {
88
+            $r = $calendar->search($pattern, $searchProperties, $options, $limit, $offset);
89
+            foreach ($r as $o) {
90
+                $o['calendar-key'] = $calendar->getKey();
91
+                $result[] = $o;
92
+            }
93
+        }
94
+
95
+        return $result;
96
+    }
97
+
98
+    /**
99
+     * Check if calendars are available
100
+     *
101
+     * @return bool true if enabled, false if not
102
+     * @since 13.0.0
103
+     */
104
+    public function isEnabled(): bool {
105
+        return !empty($this->calendars) || !empty($this->calendarLoaders);
106
+    }
107
+
108
+    /**
109
+     * Registers a calendar
110
+     *
111
+     * @since 13.0.0
112
+     */
113
+    public function registerCalendar(ICalendar $calendar): void {
114
+        $this->calendars[$calendar->getKey()] = $calendar;
115
+    }
116
+
117
+    /**
118
+     * Unregisters a calendar
119
+     *
120
+     * @since 13.0.0
121
+     */
122
+    public function unregisterCalendar(ICalendar $calendar): void {
123
+        unset($this->calendars[$calendar->getKey()]);
124
+    }
125
+
126
+    /**
127
+     * In order to improve lazy loading a closure can be registered which will be called in case
128
+     * calendars are actually requested
129
+     *
130
+     * @since 13.0.0
131
+     */
132
+    public function register(\Closure $callable): void {
133
+        $this->calendarLoaders[] = $callable;
134
+    }
135
+
136
+    /**
137
+     * @return ICalendar[]
138
+     *
139
+     * @since 13.0.0
140
+     */
141
+    public function getCalendars(): array {
142
+        $this->loadCalendars();
143
+
144
+        return array_values($this->calendars);
145
+    }
146
+
147
+    /**
148
+     * removes all registered calendar instances
149
+     *
150
+     * @since 13.0.0
151
+     */
152
+    public function clear(): void {
153
+        $this->calendars = [];
154
+        $this->calendarLoaders = [];
155
+    }
156
+
157
+    /**
158
+     * loads all calendars
159
+     */
160
+    private function loadCalendars(): void {
161
+        foreach ($this->calendarLoaders as $callable) {
162
+            $callable($this);
163
+        }
164
+        $this->calendarLoaders = [];
165
+    }
166
+
167
+    /**
168
+     * @return ICreateFromString[]
169
+     */
170
+    public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array {
171
+        $context = $this->coordinator->getRegistrationContext();
172
+        if ($context === null) {
173
+            return [];
174
+        }
175
+
176
+        return array_merge(
177
+            ...array_map(function ($registration) use ($principalUri, $calendarUris) {
178
+                try {
179
+                    /** @var ICalendarProvider $provider */
180
+                    $provider = $this->container->get($registration->getService());
181
+                } catch (Throwable $e) {
182
+                    $this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
183
+                        'exception' => $e,
184
+                    ]);
185
+                    return [];
186
+                }
187
+
188
+                return $provider->getCalendars($principalUri, $calendarUris);
189
+            }, $context->getCalendarProviders())
190
+        );
191
+    }
192
+
193
+    public function searchForPrincipal(ICalendarQuery $query): array {
194
+        /** @var CalendarQuery $query */
195
+        $calendars = $this->getCalendarsForPrincipal(
196
+            $query->getPrincipalUri(),
197
+            $query->getCalendarUris(),
198
+        );
199
+
200
+        $results = [];
201
+        foreach ($calendars as $calendar) {
202
+            $r = $calendar->search(
203
+                $query->getSearchPattern() ?? '',
204
+                $query->getSearchProperties(),
205
+                $query->getOptions(),
206
+                $query->getLimit(),
207
+                $query->getOffset()
208
+            );
209
+
210
+            foreach ($r as $o) {
211
+                $o['calendar-key'] = $calendar->getKey();
212
+                $o['calendar-uri'] = $calendar->getUri();
213
+                $results[] = $o;
214
+            }
215
+        }
216
+        return $results;
217
+    }
218
+
219
+    public function newQuery(string $principalUri): ICalendarQuery {
220
+        return new CalendarQuery($principalUri);
221
+    }
222
+
223
+    /**
224
+     * @since 31.0.0
225
+     * @throws \OCP\DB\Exception
226
+     */
227
+    public function handleIMipRequest(
228
+        string $principalUri,
229
+        string $sender,
230
+        string $recipient,
231
+        string $calendarData,
232
+    ): bool {
233
+
234
+        $userCalendars = $this->getCalendarsForPrincipal($principalUri);
235
+        if (empty($userCalendars)) {
236
+            $this->logger->warning('iMip message could not be processed because user has no calendars');
237
+            return false;
238
+        }
239 239
 		
240
-		try {
241
-			/** @var VCalendar $vObject|null */
242
-			$calendarObject = Reader::read($calendarData);
243
-		} catch (ParseException $e) {
244
-			$this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
245
-			return false;
246
-		}
247
-
248
-		if (!isset($calendarObject->METHOD) || $calendarObject->METHOD->getValue() !== 'REQUEST') {
249
-			$this->logger->warning('iMip message contains an incorrect or invalid method');
250
-			return false;
251
-		}
252
-
253
-		if (!isset($calendarObject->VEVENT)) {
254
-			$this->logger->warning('iMip message contains no event');
255
-			return false;
256
-		}
257
-
258
-		/** @var VEvent|null $vEvent */
259
-		$eventObject = $calendarObject->VEVENT;
260
-
261
-		if (!isset($eventObject->UID)) {
262
-			$this->logger->warning('iMip message event dose not contains a UID');
263
-			return false;
264
-		}
265
-
266
-		if (!isset($eventObject->ORGANIZER)) {
267
-			$this->logger->warning('iMip message event dose not contains an organizer');
268
-			return false;
269
-		}
270
-
271
-		if (!isset($eventObject->ATTENDEE)) {
272
-			$this->logger->warning('iMip message event dose not contains any attendees');
273
-			return false;
274
-		}
275
-
276
-		foreach ($eventObject->ATTENDEE as $entry) {
277
-			$address = trim(str_replace('mailto:', '', $entry->getValue()));
278
-			if ($address === $recipient) {
279
-				$attendee = $address;
280
-				break;
281
-			}
282
-		}
283
-		if (!isset($attendee)) {
284
-			$this->logger->warning('iMip message event does not contain a attendee that matches the recipient');
285
-			return false;
286
-		}
287
-
288
-		foreach ($userCalendars as $calendar) {
289
-
290
-			if (!$calendar instanceof ICalendarIsWritable && !$calendar instanceof ICalendarIsShared) {
291
-				continue;
292
-			}
293
-
294
-			if ($calendar->isDeleted() || !$calendar->isWritable() || $calendar->isShared()) {
295
-				continue;
296
-			}
297
-
298
-			if (!empty($calendar->search($recipient, ['ATTENDEE'], ['uid' => $eventObject->UID->getValue()]))) {
299
-				try {
300
-					if ($calendar instanceof IHandleImipMessage) {
301
-						$calendar->handleIMipMessage('', $calendarData);
302
-					}
303
-					return true;
304
-				} catch (CalendarException $e) {
305
-					$this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
306
-					return false;
307
-				}
308
-			}
309
-		}
310
-
311
-		$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar');
312
-		return false;
313
-	}
314
-
315
-	/**
316
-	 * @throws \OCP\DB\Exception
317
-	 */
318
-	public function handleIMipReply(
319
-		string $principalUri,
320
-		string $sender,
321
-		string $recipient,
322
-		string $calendarData,
323
-	): bool {
324
-
325
-		$calendars = $this->getCalendarsForPrincipal($principalUri);
326
-		if (empty($calendars)) {
327
-			$this->logger->warning('iMip message could not be processed because user has no calendars');
328
-			return false;
329
-		}
330
-
331
-		try {
332
-			/** @var VCalendar $vObject|null */
333
-			$vObject = Reader::read($calendarData);
334
-		} catch (ParseException $e) {
335
-			$this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
336
-			return false;
337
-		}
338
-
339
-		if ($vObject === null) {
340
-			$this->logger->warning('iMip message contains an invalid calendar object');
341
-			return false;
342
-		}
343
-
344
-		if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'REPLY') {
345
-			$this->logger->warning('iMip message contains an incorrect or invalid method');
346
-			return false;
347
-		}
348
-
349
-		if (!isset($vObject->VEVENT)) {
350
-			$this->logger->warning('iMip message contains no event');
351
-			return false;
352
-		}
353
-
354
-		/** @var VEvent|null $vEvent */
355
-		$vEvent = $vObject->VEVENT;
356
-
357
-		if (!isset($vEvent->UID)) {
358
-			$this->logger->warning('iMip message event dose not contains a UID');
359
-			return false;
360
-		}
361
-
362
-		if (!isset($vEvent->ORGANIZER)) {
363
-			$this->logger->warning('iMip message event dose not contains an organizer');
364
-			return false;
365
-		}
366
-
367
-		if (!isset($vEvent->ATTENDEE)) {
368
-			$this->logger->warning('iMip message event dose not contains any attendees');
369
-			return false;
370
-		}
371
-
372
-		// check if mail recipient and organizer are one and the same
373
-		$organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
374
-
375
-		if (strcasecmp($recipient, $organizer) !== 0) {
376
-			$this->logger->warning('iMip message event could not be processed because recipient and ORGANIZER must be identical');
377
-			return false;
378
-		}
379
-
380
-		//check if the event is in the future
381
-		/** @var DateTime $eventTime */
382
-		$eventTime = $vEvent->{'DTSTART'};
383
-		if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences
384
-			$this->logger->warning('iMip message event could not be processed because the event is in the past');
385
-			return false;
386
-		}
387
-
388
-		$found = null;
389
-		// if the attendee has been found in at least one calendar event with the UID of the iMIP event
390
-		// we process it.
391
-		// Benefit: no attendee lost
392
-		// Drawback: attendees that have been deleted will still be able to update their partstat
393
-		foreach ($calendars as $calendar) {
394
-			// We should not search in writable calendars
395
-			if ($calendar instanceof IHandleImipMessage) {
396
-				$o = $calendar->search($sender, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
397
-				if (!empty($o)) {
398
-					$found = $calendar;
399
-					$name = $o[0]['uri'];
400
-					break;
401
-				}
402
-			}
403
-		}
404
-
405
-		if (empty($found)) {
406
-			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
407
-			return false;
408
-		}
409
-
410
-		try {
411
-			$found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
412
-		} catch (CalendarException $e) {
413
-			$this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
414
-			return false;
415
-		}
416
-		return true;
417
-	}
418
-
419
-	/**
420
-	 * @since 25.0.0
421
-	 * @throws \OCP\DB\Exception
422
-	 */
423
-	public function handleIMipCancel(
424
-		string $principalUri,
425
-		string $sender,
426
-		?string $replyTo,
427
-		string $recipient,
428
-		string $calendarData,
429
-	): bool {
430
-
431
-		$calendars = $this->getCalendarsForPrincipal($principalUri);
432
-		if (empty($calendars)) {
433
-			$this->logger->warning('iMip message could not be processed because user has no calendars');
434
-			return false;
435
-		}
436
-
437
-		try {
438
-			/** @var VCalendar $vObject|null */
439
-			$vObject = Reader::read($calendarData);
440
-		} catch (ParseException $e) {
441
-			$this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
442
-			return false;
443
-		}
444
-
445
-		if ($vObject === null) {
446
-			$this->logger->warning('iMip message contains an invalid calendar object');
447
-			return false;
448
-		}
449
-
450
-		if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'CANCEL') {
451
-			$this->logger->warning('iMip message contains an incorrect or invalid method');
452
-			return false;
453
-		}
454
-
455
-		if (!isset($vObject->VEVENT)) {
456
-			$this->logger->warning('iMip message contains no event');
457
-			return false;
458
-		}
459
-
460
-		/** @var VEvent|null $vEvent */
461
-		$vEvent = $vObject->{'VEVENT'};
462
-
463
-		if (!isset($vEvent->UID)) {
464
-			$this->logger->warning('iMip message event dose not contains a UID');
465
-			return false;
466
-		}
467
-
468
-		if (!isset($vEvent->ORGANIZER)) {
469
-			$this->logger->warning('iMip message event dose not contains an organizer');
470
-			return false;
471
-		}
472
-
473
-		if (!isset($vEvent->ATTENDEE)) {
474
-			$this->logger->warning('iMip message event dose not contains any attendees');
475
-			return false;
476
-		}
477
-
478
-		$attendee = substr($vEvent->{'ATTENDEE'}->getValue(), 7);
479
-		if (strcasecmp($recipient, $attendee) !== 0) {
480
-			$this->logger->warning('iMip message event could not be processed because recipient must be an ATTENDEE of this event');
481
-			return false;
482
-		}
483
-
484
-		// Thirdly, we need to compare the email address the CANCEL is coming from (in Mail)
485
-		// or the Reply- To Address submitted with the CANCEL email
486
-		// to the email address in the ORGANIZER.
487
-		// We don't want to accept a CANCEL request from just anyone
488
-		$organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
489
-		$isNotOrganizer = ($replyTo !== null) ? (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) : (strcasecmp($sender, $organizer) !== 0);
490
-		if ($isNotOrganizer) {
491
-			$this->logger->warning('iMip message event could not be processed because sender must be the ORGANIZER of this event');
492
-			return false;
493
-		}
494
-
495
-		//check if the event is in the future
496
-		/** @var DateTime $eventTime */
497
-		$eventTime = $vEvent->{'DTSTART'};
498
-		if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences
499
-			$this->logger->warning('iMip message event could not be processed because the event is in the past');
500
-			return false;
501
-		}
502
-
503
-		$found = null;
504
-		// if the attendee has been found in at least one calendar event with the UID of the iMIP event
505
-		// we process it.
506
-		// Benefit: no attendee lost
507
-		// Drawback: attendees that have been deleted will still be able to update their partstat
508
-		foreach ($calendars as $calendar) {
509
-			// We should not search in writable calendars
510
-			if ($calendar instanceof IHandleImipMessage) {
511
-				$o = $calendar->search($recipient, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
512
-				if (!empty($o)) {
513
-					$found = $calendar;
514
-					$name = $o[0]['uri'];
515
-					break;
516
-				}
517
-			}
518
-		}
519
-
520
-		if (empty($found)) {
521
-			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
522
-			return false;
523
-		}
524
-
525
-		try {
526
-			$found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
527
-			return true;
528
-		} catch (CalendarException $e) {
529
-			$this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
530
-			return false;
531
-		}
532
-	}
533
-
534
-	public function createEventBuilder(): ICalendarEventBuilder {
535
-		$uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
536
-		return new CalendarEventBuilder($uid, $this->timeFactory);
537
-	}
538
-
539
-	public function checkAvailability(
540
-		DateTimeInterface $start,
541
-		DateTimeInterface $end,
542
-		IUser $organizer,
543
-		array $attendees,
544
-	): array {
545
-		$organizerMailto = 'mailto:' . $organizer->getEMailAddress();
546
-		$request = new VCalendar();
547
-		$request->METHOD = 'REQUEST';
548
-		$request->add('VFREEBUSY', [
549
-			'DTSTART' => $start,
550
-			'DTEND' => $end,
551
-			'ORGANIZER' => $organizerMailto,
552
-			'ATTENDEE' => $organizerMailto,
553
-		]);
554
-
555
-		$mailtoLen = strlen('mailto:');
556
-		foreach ($attendees as $attendee) {
557
-			if (str_starts_with($attendee, 'mailto:')) {
558
-				$attendee = substr($attendee, $mailtoLen);
559
-			}
560
-
561
-			$attendeeUsers = $this->userManager->getByEmail($attendee);
562
-			if ($attendeeUsers === []) {
563
-				continue;
564
-			}
565
-
566
-			$request->VFREEBUSY->add('ATTENDEE', "mailto:$attendee");
567
-		}
568
-
569
-		$organizerUid = $organizer->getUID();
570
-		$server = $this->serverFactory->createAttendeeAvailabilityServer();
571
-		/** @var CustomPrincipalPlugin $plugin */
572
-		$plugin = $server->getPlugin('auth');
573
-		$plugin->setCurrentPrincipal("principals/users/$organizerUid");
574
-
575
-		$request = new Request(
576
-			'POST',
577
-			"/calendars/$organizerUid/outbox/",
578
-			[
579
-				'Content-Type' => 'text/calendar',
580
-				'Depth' => 0,
581
-			],
582
-			$request->serialize(),
583
-		);
584
-		$response = new Response();
585
-		$server->invokeMethod($request, $response, false);
586
-
587
-		$xmlService = new \Sabre\Xml\Service();
588
-		$xmlService->elementMap = [
589
-			'{urn:ietf:params:xml:ns:caldav}response' => 'Sabre\Xml\Deserializer\keyValue',
590
-			'{urn:ietf:params:xml:ns:caldav}recipient' => 'Sabre\Xml\Deserializer\keyValue',
591
-		];
592
-		$parsedResponse = $xmlService->parse($response->getBodyAsString());
593
-
594
-		$result = [];
595
-		foreach ($parsedResponse as $freeBusyResponse) {
596
-			$freeBusyResponse = $freeBusyResponse['value'];
597
-			if ($freeBusyResponse['{urn:ietf:params:xml:ns:caldav}request-status'] !== '2.0;Success') {
598
-				continue;
599
-			}
600
-
601
-			$freeBusyResponseData = \Sabre\VObject\Reader::read(
602
-				$freeBusyResponse['{urn:ietf:params:xml:ns:caldav}calendar-data']
603
-			);
604
-
605
-			$attendee = substr(
606
-				$freeBusyResponse['{urn:ietf:params:xml:ns:caldav}recipient']['{DAV:}href'],
607
-				$mailtoLen,
608
-			);
609
-
610
-			$vFreeBusy = $freeBusyResponseData->VFREEBUSY;
611
-			if (!($vFreeBusy instanceof VFreeBusy)) {
612
-				continue;
613
-			}
614
-
615
-			// TODO: actually check values of FREEBUSY properties to find a free slot
616
-			$result[] = new AvailabilityResult($attendee, $vFreeBusy->isFree($start, $end));
617
-		}
618
-
619
-		return $result;
620
-	}
240
+        try {
241
+            /** @var VCalendar $vObject|null */
242
+            $calendarObject = Reader::read($calendarData);
243
+        } catch (ParseException $e) {
244
+            $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
245
+            return false;
246
+        }
247
+
248
+        if (!isset($calendarObject->METHOD) || $calendarObject->METHOD->getValue() !== 'REQUEST') {
249
+            $this->logger->warning('iMip message contains an incorrect or invalid method');
250
+            return false;
251
+        }
252
+
253
+        if (!isset($calendarObject->VEVENT)) {
254
+            $this->logger->warning('iMip message contains no event');
255
+            return false;
256
+        }
257
+
258
+        /** @var VEvent|null $vEvent */
259
+        $eventObject = $calendarObject->VEVENT;
260
+
261
+        if (!isset($eventObject->UID)) {
262
+            $this->logger->warning('iMip message event dose not contains a UID');
263
+            return false;
264
+        }
265
+
266
+        if (!isset($eventObject->ORGANIZER)) {
267
+            $this->logger->warning('iMip message event dose not contains an organizer');
268
+            return false;
269
+        }
270
+
271
+        if (!isset($eventObject->ATTENDEE)) {
272
+            $this->logger->warning('iMip message event dose not contains any attendees');
273
+            return false;
274
+        }
275
+
276
+        foreach ($eventObject->ATTENDEE as $entry) {
277
+            $address = trim(str_replace('mailto:', '', $entry->getValue()));
278
+            if ($address === $recipient) {
279
+                $attendee = $address;
280
+                break;
281
+            }
282
+        }
283
+        if (!isset($attendee)) {
284
+            $this->logger->warning('iMip message event does not contain a attendee that matches the recipient');
285
+            return false;
286
+        }
287
+
288
+        foreach ($userCalendars as $calendar) {
289
+
290
+            if (!$calendar instanceof ICalendarIsWritable && !$calendar instanceof ICalendarIsShared) {
291
+                continue;
292
+            }
293
+
294
+            if ($calendar->isDeleted() || !$calendar->isWritable() || $calendar->isShared()) {
295
+                continue;
296
+            }
297
+
298
+            if (!empty($calendar->search($recipient, ['ATTENDEE'], ['uid' => $eventObject->UID->getValue()]))) {
299
+                try {
300
+                    if ($calendar instanceof IHandleImipMessage) {
301
+                        $calendar->handleIMipMessage('', $calendarData);
302
+                    }
303
+                    return true;
304
+                } catch (CalendarException $e) {
305
+                    $this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
306
+                    return false;
307
+                }
308
+            }
309
+        }
310
+
311
+        $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar');
312
+        return false;
313
+    }
314
+
315
+    /**
316
+     * @throws \OCP\DB\Exception
317
+     */
318
+    public function handleIMipReply(
319
+        string $principalUri,
320
+        string $sender,
321
+        string $recipient,
322
+        string $calendarData,
323
+    ): bool {
324
+
325
+        $calendars = $this->getCalendarsForPrincipal($principalUri);
326
+        if (empty($calendars)) {
327
+            $this->logger->warning('iMip message could not be processed because user has no calendars');
328
+            return false;
329
+        }
330
+
331
+        try {
332
+            /** @var VCalendar $vObject|null */
333
+            $vObject = Reader::read($calendarData);
334
+        } catch (ParseException $e) {
335
+            $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
336
+            return false;
337
+        }
338
+
339
+        if ($vObject === null) {
340
+            $this->logger->warning('iMip message contains an invalid calendar object');
341
+            return false;
342
+        }
343
+
344
+        if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'REPLY') {
345
+            $this->logger->warning('iMip message contains an incorrect or invalid method');
346
+            return false;
347
+        }
348
+
349
+        if (!isset($vObject->VEVENT)) {
350
+            $this->logger->warning('iMip message contains no event');
351
+            return false;
352
+        }
353
+
354
+        /** @var VEvent|null $vEvent */
355
+        $vEvent = $vObject->VEVENT;
356
+
357
+        if (!isset($vEvent->UID)) {
358
+            $this->logger->warning('iMip message event dose not contains a UID');
359
+            return false;
360
+        }
361
+
362
+        if (!isset($vEvent->ORGANIZER)) {
363
+            $this->logger->warning('iMip message event dose not contains an organizer');
364
+            return false;
365
+        }
366
+
367
+        if (!isset($vEvent->ATTENDEE)) {
368
+            $this->logger->warning('iMip message event dose not contains any attendees');
369
+            return false;
370
+        }
371
+
372
+        // check if mail recipient and organizer are one and the same
373
+        $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
374
+
375
+        if (strcasecmp($recipient, $organizer) !== 0) {
376
+            $this->logger->warning('iMip message event could not be processed because recipient and ORGANIZER must be identical');
377
+            return false;
378
+        }
379
+
380
+        //check if the event is in the future
381
+        /** @var DateTime $eventTime */
382
+        $eventTime = $vEvent->{'DTSTART'};
383
+        if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences
384
+            $this->logger->warning('iMip message event could not be processed because the event is in the past');
385
+            return false;
386
+        }
387
+
388
+        $found = null;
389
+        // if the attendee has been found in at least one calendar event with the UID of the iMIP event
390
+        // we process it.
391
+        // Benefit: no attendee lost
392
+        // Drawback: attendees that have been deleted will still be able to update their partstat
393
+        foreach ($calendars as $calendar) {
394
+            // We should not search in writable calendars
395
+            if ($calendar instanceof IHandleImipMessage) {
396
+                $o = $calendar->search($sender, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
397
+                if (!empty($o)) {
398
+                    $found = $calendar;
399
+                    $name = $o[0]['uri'];
400
+                    break;
401
+                }
402
+            }
403
+        }
404
+
405
+        if (empty($found)) {
406
+            $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
407
+            return false;
408
+        }
409
+
410
+        try {
411
+            $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
412
+        } catch (CalendarException $e) {
413
+            $this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
414
+            return false;
415
+        }
416
+        return true;
417
+    }
418
+
419
+    /**
420
+     * @since 25.0.0
421
+     * @throws \OCP\DB\Exception
422
+     */
423
+    public function handleIMipCancel(
424
+        string $principalUri,
425
+        string $sender,
426
+        ?string $replyTo,
427
+        string $recipient,
428
+        string $calendarData,
429
+    ): bool {
430
+
431
+        $calendars = $this->getCalendarsForPrincipal($principalUri);
432
+        if (empty($calendars)) {
433
+            $this->logger->warning('iMip message could not be processed because user has no calendars');
434
+            return false;
435
+        }
436
+
437
+        try {
438
+            /** @var VCalendar $vObject|null */
439
+            $vObject = Reader::read($calendarData);
440
+        } catch (ParseException $e) {
441
+            $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]);
442
+            return false;
443
+        }
444
+
445
+        if ($vObject === null) {
446
+            $this->logger->warning('iMip message contains an invalid calendar object');
447
+            return false;
448
+        }
449
+
450
+        if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'CANCEL') {
451
+            $this->logger->warning('iMip message contains an incorrect or invalid method');
452
+            return false;
453
+        }
454
+
455
+        if (!isset($vObject->VEVENT)) {
456
+            $this->logger->warning('iMip message contains no event');
457
+            return false;
458
+        }
459
+
460
+        /** @var VEvent|null $vEvent */
461
+        $vEvent = $vObject->{'VEVENT'};
462
+
463
+        if (!isset($vEvent->UID)) {
464
+            $this->logger->warning('iMip message event dose not contains a UID');
465
+            return false;
466
+        }
467
+
468
+        if (!isset($vEvent->ORGANIZER)) {
469
+            $this->logger->warning('iMip message event dose not contains an organizer');
470
+            return false;
471
+        }
472
+
473
+        if (!isset($vEvent->ATTENDEE)) {
474
+            $this->logger->warning('iMip message event dose not contains any attendees');
475
+            return false;
476
+        }
477
+
478
+        $attendee = substr($vEvent->{'ATTENDEE'}->getValue(), 7);
479
+        if (strcasecmp($recipient, $attendee) !== 0) {
480
+            $this->logger->warning('iMip message event could not be processed because recipient must be an ATTENDEE of this event');
481
+            return false;
482
+        }
483
+
484
+        // Thirdly, we need to compare the email address the CANCEL is coming from (in Mail)
485
+        // or the Reply- To Address submitted with the CANCEL email
486
+        // to the email address in the ORGANIZER.
487
+        // We don't want to accept a CANCEL request from just anyone
488
+        $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
489
+        $isNotOrganizer = ($replyTo !== null) ? (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) : (strcasecmp($sender, $organizer) !== 0);
490
+        if ($isNotOrganizer) {
491
+            $this->logger->warning('iMip message event could not be processed because sender must be the ORGANIZER of this event');
492
+            return false;
493
+        }
494
+
495
+        //check if the event is in the future
496
+        /** @var DateTime $eventTime */
497
+        $eventTime = $vEvent->{'DTSTART'};
498
+        if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences
499
+            $this->logger->warning('iMip message event could not be processed because the event is in the past');
500
+            return false;
501
+        }
502
+
503
+        $found = null;
504
+        // if the attendee has been found in at least one calendar event with the UID of the iMIP event
505
+        // we process it.
506
+        // Benefit: no attendee lost
507
+        // Drawback: attendees that have been deleted will still be able to update their partstat
508
+        foreach ($calendars as $calendar) {
509
+            // We should not search in writable calendars
510
+            if ($calendar instanceof IHandleImipMessage) {
511
+                $o = $calendar->search($recipient, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
512
+                if (!empty($o)) {
513
+                    $found = $calendar;
514
+                    $name = $o[0]['uri'];
515
+                    break;
516
+                }
517
+            }
518
+        }
519
+
520
+        if (empty($found)) {
521
+            $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
522
+            return false;
523
+        }
524
+
525
+        try {
526
+            $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
527
+            return true;
528
+        } catch (CalendarException $e) {
529
+            $this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]);
530
+            return false;
531
+        }
532
+    }
533
+
534
+    public function createEventBuilder(): ICalendarEventBuilder {
535
+        $uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
536
+        return new CalendarEventBuilder($uid, $this->timeFactory);
537
+    }
538
+
539
+    public function checkAvailability(
540
+        DateTimeInterface $start,
541
+        DateTimeInterface $end,
542
+        IUser $organizer,
543
+        array $attendees,
544
+    ): array {
545
+        $organizerMailto = 'mailto:' . $organizer->getEMailAddress();
546
+        $request = new VCalendar();
547
+        $request->METHOD = 'REQUEST';
548
+        $request->add('VFREEBUSY', [
549
+            'DTSTART' => $start,
550
+            'DTEND' => $end,
551
+            'ORGANIZER' => $organizerMailto,
552
+            'ATTENDEE' => $organizerMailto,
553
+        ]);
554
+
555
+        $mailtoLen = strlen('mailto:');
556
+        foreach ($attendees as $attendee) {
557
+            if (str_starts_with($attendee, 'mailto:')) {
558
+                $attendee = substr($attendee, $mailtoLen);
559
+            }
560
+
561
+            $attendeeUsers = $this->userManager->getByEmail($attendee);
562
+            if ($attendeeUsers === []) {
563
+                continue;
564
+            }
565
+
566
+            $request->VFREEBUSY->add('ATTENDEE', "mailto:$attendee");
567
+        }
568
+
569
+        $organizerUid = $organizer->getUID();
570
+        $server = $this->serverFactory->createAttendeeAvailabilityServer();
571
+        /** @var CustomPrincipalPlugin $plugin */
572
+        $plugin = $server->getPlugin('auth');
573
+        $plugin->setCurrentPrincipal("principals/users/$organizerUid");
574
+
575
+        $request = new Request(
576
+            'POST',
577
+            "/calendars/$organizerUid/outbox/",
578
+            [
579
+                'Content-Type' => 'text/calendar',
580
+                'Depth' => 0,
581
+            ],
582
+            $request->serialize(),
583
+        );
584
+        $response = new Response();
585
+        $server->invokeMethod($request, $response, false);
586
+
587
+        $xmlService = new \Sabre\Xml\Service();
588
+        $xmlService->elementMap = [
589
+            '{urn:ietf:params:xml:ns:caldav}response' => 'Sabre\Xml\Deserializer\keyValue',
590
+            '{urn:ietf:params:xml:ns:caldav}recipient' => 'Sabre\Xml\Deserializer\keyValue',
591
+        ];
592
+        $parsedResponse = $xmlService->parse($response->getBodyAsString());
593
+
594
+        $result = [];
595
+        foreach ($parsedResponse as $freeBusyResponse) {
596
+            $freeBusyResponse = $freeBusyResponse['value'];
597
+            if ($freeBusyResponse['{urn:ietf:params:xml:ns:caldav}request-status'] !== '2.0;Success') {
598
+                continue;
599
+            }
600
+
601
+            $freeBusyResponseData = \Sabre\VObject\Reader::read(
602
+                $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}calendar-data']
603
+            );
604
+
605
+            $attendee = substr(
606
+                $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}recipient']['{DAV:}href'],
607
+                $mailtoLen,
608
+            );
609
+
610
+            $vFreeBusy = $freeBusyResponseData->VFREEBUSY;
611
+            if (!($vFreeBusy instanceof VFreeBusy)) {
612
+                continue;
613
+            }
614
+
615
+            // TODO: actually check values of FREEBUSY properties to find a free slot
616
+            $result[] = new AvailabilityResult($attendee, $vFreeBusy->isFree($start, $end));
617
+        }
618
+
619
+        return $result;
620
+    }
621 621
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -174,12 +174,12 @@  discard block
 block discarded – undo
174 174
 		}
175 175
 
176 176
 		return array_merge(
177
-			...array_map(function ($registration) use ($principalUri, $calendarUris) {
177
+			...array_map(function($registration) use ($principalUri, $calendarUris) {
178 178
 				try {
179 179
 					/** @var ICalendarProvider $provider */
180 180
 					$provider = $this->container->get($registration->getService());
181 181
 				} catch (Throwable $e) {
182
-					$this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
182
+					$this->logger->error('Could not load calendar provider '.$registration->getService().': '.$e->getMessage(), [
183 183
 						'exception' => $e,
184 184
 					]);
185 185
 					return [];
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 		}
404 404
 
405 405
 		if (empty($found)) {
406
-			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
406
+			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar '.$principalUri.'and UID'.$vEvent->{'UID'}->getValue());
407 407
 			return false;
408 408
 		}
409 409
 
@@ -518,7 +518,7 @@  discard block
 block discarded – undo
518 518
 		}
519 519
 
520 520
 		if (empty($found)) {
521
-			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
521
+			$this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar '.$principalUri.'and UID'.$vEvent->{'UID'}->getValue());
522 522
 			return false;
523 523
 		}
524 524
 
@@ -542,7 +542,7 @@  discard block
 block discarded – undo
542 542
 		IUser $organizer,
543 543
 		array $attendees,
544 544
 	): array {
545
-		$organizerMailto = 'mailto:' . $organizer->getEMailAddress();
545
+		$organizerMailto = 'mailto:'.$organizer->getEMailAddress();
546 546
 		$request = new VCalendar();
547 547
 		$request->METHOD = 'REQUEST';
548 548
 		$request->add('VFREEBUSY', [
Please login to merge, or discard this patch.
tests/lib/Calendar/ManagerTest.php 2 patches
Indentation   +1719 added lines, -1719 removed lines patch added patch discarded remove patch
@@ -36,1591 +36,1591 @@  discard block
 block discarded – undo
36 36
 }
37 37
 
38 38
 class ManagerTest extends TestCase {
39
-	/** @var Coordinator&MockObject */
40
-	private $coordinator;
41
-
42
-	/** @var ContainerInterface&MockObject */
43
-	private $container;
44
-
45
-	/** @var LoggerInterface&MockObject */
46
-	private $logger;
47
-
48
-	/** @var Manager */
49
-	private $manager;
50
-
51
-	/** @var ITimeFactory&MockObject */
52
-	private $time;
53
-
54
-	/** @var ISecureRandom&MockObject */
55
-	private ISecureRandom $secureRandom;
56
-
57
-	private IUserManager&MockObject $userManager;
58
-	private ServerFactory&MockObject $serverFactory;
59
-
60
-	private VCalendar $vCalendar1a;
61
-	private VCalendar $vCalendar2a;
62
-	private VCalendar $vCalendar3a;
63
-
64
-	protected function setUp(): void {
65
-		parent::setUp();
66
-
67
-		$this->coordinator = $this->createMock(Coordinator::class);
68
-		$this->container = $this->createMock(ContainerInterface::class);
69
-		$this->logger = $this->createMock(LoggerInterface::class);
70
-		$this->time = $this->createMock(ITimeFactory::class);
71
-		$this->secureRandom = $this->createMock(ISecureRandom::class);
72
-		$this->userManager = $this->createMock(IUserManager::class);
73
-		$this->serverFactory = $this->createMock(ServerFactory::class);
74
-
75
-		$this->manager = new Manager(
76
-			$this->coordinator,
77
-			$this->container,
78
-			$this->logger,
79
-			$this->time,
80
-			$this->secureRandom,
81
-			$this->userManager,
82
-			$this->serverFactory,
83
-		);
84
-
85
-		// construct calendar with a 1 hour event and same start/end time zones
86
-		$this->vCalendar1a = new VCalendar();
87
-		/** @var VEvent $vEvent */
88
-		$vEvent = $this->vCalendar1a->add('VEVENT', []);
89
-		$vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
90
-		$vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
91
-		$vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
92
-		$vEvent->add('SUMMARY', 'Test Event');
93
-		$vEvent->add('SEQUENCE', 3);
94
-		$vEvent->add('STATUS', 'CONFIRMED');
95
-		$vEvent->add('TRANSP', 'OPAQUE');
96
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'Organizer']);
97
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
98
-			'CN' => 'Attendee One',
99
-			'CUTYPE' => 'INDIVIDUAL',
100
-			'PARTSTAT' => 'NEEDS-ACTION',
101
-			'ROLE' => 'REQ-PARTICIPANT',
102
-			'RSVP' => 'TRUE'
103
-		]);
104
-
105
-		// construct calendar with a event for reply
106
-		$this->vCalendar2a = new VCalendar();
107
-		/** @var VEvent $vEvent */
108
-		$vEvent = $this->vCalendar2a->add('VEVENT', []);
109
-		$vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
110
-		$vEvent->add('DTSTART', '20210820');
111
-		$vEvent->add('DTEND', '20220821');
112
-		$vEvent->add('SUMMARY', 'berry basket');
113
-		$vEvent->add('SEQUENCE', 3);
114
-		$vEvent->add('STATUS', 'CONFIRMED');
115
-		$vEvent->add('TRANSP', 'OPAQUE');
116
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
117
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
118
-			'CN' => '[email protected]',
119
-			'CUTYPE' => 'INDIVIDUAL',
120
-			'ROLE' => 'REQ-PARTICIPANT',
121
-			'PARTSTAT' => 'ACCEPTED',
122
-		]);
123
-
124
-		// construct calendar with a event for reply
125
-		$this->vCalendar3a = new VCalendar();
126
-		/** @var VEvent $vEvent */
127
-		$vEvent = $this->vCalendar3a->add('VEVENT', []);
128
-		$vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
129
-		$vEvent->add('DTSTART', '20210820');
130
-		$vEvent->add('DTEND', '20220821');
131
-		$vEvent->add('SUMMARY', 'berry basket');
132
-		$vEvent->add('SEQUENCE', 3);
133
-		$vEvent->add('STATUS', 'CANCELLED');
134
-		$vEvent->add('TRANSP', 'OPAQUE');
135
-		$vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
136
-		$vEvent->add('ATTENDEE', 'mailto:[email protected]', [
137
-			'CN' => '[email protected]',
138
-			'CUTYPE' => 'INDIVIDUAL',
139
-			'ROLE' => 'REQ-PARTICIPANT',
140
-			'PARTSTAT' => 'ACCEPTED',
141
-		]);
142
-
143
-	}
144
-
145
-	/**
146
-	 * @dataProvider searchProvider
147
-	 */
148
-	public function testSearch($search1, $search2, $expected): void {
149
-		/** @var ICalendar | MockObject $calendar1 */
150
-		$calendar1 = $this->createMock(ICalendar::class);
151
-		$calendar1->method('getKey')->willReturn('simple:1');
152
-		$calendar1->expects($this->once())
153
-			->method('search')
154
-			->with('', [], [], null, null)
155
-			->willReturn($search1);
156
-
157
-		/** @var ICalendar | MockObject $calendar2 */
158
-		$calendar2 = $this->createMock(ICalendar::class);
159
-		$calendar2->method('getKey')->willReturn('simple:2');
160
-		$calendar2->expects($this->once())
161
-			->method('search')
162
-			->with('', [], [], null, null)
163
-			->willReturn($search2);
164
-
165
-		$this->manager->registerCalendar($calendar1);
166
-		$this->manager->registerCalendar($calendar2);
167
-
168
-		$result = $this->manager->search('');
169
-		$this->assertEquals($expected, $result);
170
-	}
171
-
172
-	/**
173
-	 * @dataProvider searchProvider
174
-	 */
175
-	public function testSearchOptions($search1, $search2, $expected): void {
176
-		/** @var ICalendar | MockObject $calendar1 */
177
-		$calendar1 = $this->createMock(ICalendar::class);
178
-		$calendar1->method('getKey')->willReturn('simple:1');
179
-		$calendar1->expects($this->once())
180
-			->method('search')
181
-			->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
182
-				['timerange' => ['start' => null, 'end' => null]], 5, 20)
183
-			->willReturn($search1);
184
-
185
-		/** @var ICalendar | MockObject $calendar2 */
186
-		$calendar2 = $this->createMock(ICalendar::class);
187
-		$calendar2->method('getKey')->willReturn('simple:2');
188
-		$calendar2->expects($this->once())
189
-			->method('search')
190
-			->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
191
-				['timerange' => ['start' => null, 'end' => null]], 5, 20)
192
-			->willReturn($search2);
193
-
194
-		$this->manager->registerCalendar($calendar1);
195
-		$this->manager->registerCalendar($calendar2);
196
-
197
-		$result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
198
-			['timerange' => ['start' => null, 'end' => null]], 5, 20);
199
-		$this->assertEquals($expected, $result);
200
-	}
201
-
202
-	public function searchProvider() {
203
-		$search1 = [
204
-			[
205
-				'id' => 1,
206
-				'data' => 'foobar',
207
-			],
208
-			[
209
-				'id' => 2,
210
-				'data' => 'barfoo',
211
-			]
212
-		];
213
-		$search2 = [
214
-			[
215
-				'id' => 3,
216
-				'data' => 'blablub',
217
-			],
218
-			[
219
-				'id' => 4,
220
-				'data' => 'blubbla',
221
-			]
222
-		];
223
-
224
-		$expected = [
225
-			[
226
-				'id' => 1,
227
-				'data' => 'foobar',
228
-				'calendar-key' => 'simple:1',
229
-			],
230
-			[
231
-				'id' => 2,
232
-				'data' => 'barfoo',
233
-				'calendar-key' => 'simple:1',
234
-			],
235
-			[
236
-				'id' => 3,
237
-				'data' => 'blablub',
238
-				'calendar-key' => 'simple:2',
239
-			],
240
-			[
241
-				'id' => 4,
242
-				'data' => 'blubbla',
243
-				'calendar-key' => 'simple:2',
244
-			]
245
-		];
246
-
247
-		return [
248
-			[
249
-				$search1,
250
-				$search2,
251
-				$expected
252
-			]
253
-		];
254
-	}
255
-
256
-	public function testRegisterUnregister(): void {
257
-		/** @var ICalendar | MockObject $calendar1 */
258
-		$calendar1 = $this->createMock(ICalendar::class);
259
-		$calendar1->method('getKey')->willReturn('key1');
260
-
261
-		/** @var ICalendar | MockObject $calendar2 */
262
-		$calendar2 = $this->createMock(ICalendar::class);
263
-		$calendar2->method('getKey')->willReturn('key2');
264
-
265
-		$this->manager->registerCalendar($calendar1);
266
-		$this->manager->registerCalendar($calendar2);
267
-
268
-		$result = $this->manager->getCalendars();
269
-		$this->assertCount(2, $result);
270
-		$this->assertContains($calendar1, $result);
271
-		$this->assertContains($calendar2, $result);
272
-
273
-		$this->manager->unregisterCalendar($calendar1);
274
-
275
-		$result = $this->manager->getCalendars();
276
-		$this->assertCount(1, $result);
277
-		$this->assertContains($calendar2, $result);
278
-	}
279
-
280
-	public function testGetCalendars(): void {
281
-		/** @var ICalendar | MockObject $calendar1 */
282
-		$calendar1 = $this->createMock(ICalendar::class);
283
-		$calendar1->method('getKey')->willReturn('key1');
284
-
285
-		/** @var ICalendar | MockObject $calendar2 */
286
-		$calendar2 = $this->createMock(ICalendar::class);
287
-		$calendar2->method('getKey')->willReturn('key2');
288
-
289
-		$this->manager->registerCalendar($calendar1);
290
-		$this->manager->registerCalendar($calendar2);
291
-
292
-		$result = $this->manager->getCalendars();
293
-		$this->assertCount(2, $result);
294
-		$this->assertContains($calendar1, $result);
295
-		$this->assertContains($calendar2, $result);
296
-
297
-		$this->manager->clear();
298
-
299
-		$result = $this->manager->getCalendars();
300
-
301
-		$this->assertCount(0, $result);
302
-	}
303
-
304
-	public function testEnabledIfNot(): void {
305
-		$isEnabled = $this->manager->isEnabled();
306
-		$this->assertFalse($isEnabled);
307
-	}
308
-
309
-	public function testIfEnabledIfSo(): void {
310
-		/** @var ICalendar | MockObject $calendar */
311
-		$calendar = $this->createMock(ICalendar::class);
312
-		$this->manager->registerCalendar($calendar);
313
-
314
-		$isEnabled = $this->manager->isEnabled();
315
-		$this->assertTrue($isEnabled);
316
-	}
317
-
318
-	public function testHandleImipRequestWithNoCalendars(): void {
319
-		// construct calendar manager returns
320
-		/** @var Manager&MockObject $manager */
321
-		$manager = $this->getMockBuilder(Manager::class)
322
-			->setConstructorArgs([
323
-				$this->coordinator,
324
-				$this->container,
325
-				$this->logger,
326
-				$this->time,
327
-				$this->secureRandom,
328
-				$this->userManager,
329
-				$this->serverFactory,
330
-			])
331
-			->onlyMethods(['getCalendarsForPrincipal'])
332
-			->getMock();
333
-		$manager->expects(self::once())
334
-			->method('getCalendarsForPrincipal')
335
-			->willReturn([]);
336
-		// construct logger returns
337
-		$this->logger->expects(self::once())->method('warning')
338
-			->with('iMip message could not be processed because user has no calendars');
339
-		// construct parameters
340
-		$principalUri = 'principals/user/attendee1';
341
-		$sender = '[email protected]';
342
-		$recipient = '[email protected]';
343
-		$calendar = $this->vCalendar1a;
344
-		$calendar->add('METHOD', 'REQUEST');
345
-		// Act
346
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
347
-		// Assert
348
-		$this->assertFalse($result);
349
-	}
350
-
351
-	public function testHandleImipRequestWithInvalidData(): void {
352
-		// construct mock user calendar
353
-		$userCalendar = $this->createMock(ITestCalendar::class);
354
-		// construct mock calendar manager and returns
355
-		/** @var Manager&MockObject $manager */
356
-		$manager = $this->getMockBuilder(Manager::class)
357
-			->setConstructorArgs([
358
-				$this->coordinator,
359
-				$this->container,
360
-				$this->logger,
361
-				$this->time,
362
-				$this->secureRandom,
363
-				$this->userManager,
364
-				$this->serverFactory,
365
-			])
366
-			->onlyMethods(['getCalendarsForPrincipal'])
367
-			->getMock();
368
-		$manager->expects(self::once())
369
-			->method('getCalendarsForPrincipal')
370
-			->willReturn([$userCalendar]);
371
-		// construct logger returns
372
-		$this->logger->expects(self::once())->method('error')
373
-			->with('iMip message could not be processed because an error occurred while parsing the iMip message');
374
-		// construct parameters
375
-		$principalUri = 'principals/user/attendee1';
376
-		$sender = '[email protected]';
377
-		$recipient = '[email protected]';
378
-		// Act
379
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, 'Invalid data');
380
-		// Assert
381
-		$this->assertFalse($result);
382
-	}
383
-
384
-	public function testHandleImipRequestWithNoMethod(): void {
385
-		// construct mock user calendar
386
-		$userCalendar = $this->createMock(ITestCalendar::class);
387
-		// construct mock calendar manager and returns
388
-		/** @var Manager&MockObject $manager */
389
-		$manager = $this->getMockBuilder(Manager::class)
390
-			->setConstructorArgs([
391
-				$this->coordinator,
392
-				$this->container,
393
-				$this->logger,
394
-				$this->time,
395
-				$this->secureRandom,
396
-				$this->userManager,
397
-				$this->serverFactory,
398
-			])
399
-			->onlyMethods(['getCalendarsForPrincipal'])
400
-			->getMock();
401
-		$manager->expects(self::once())
402
-			->method('getCalendarsForPrincipal')
403
-			->willReturn([$userCalendar]);
404
-		// construct logger returns
405
-		$this->logger->expects(self::once())->method('warning')
406
-			->with('iMip message contains an incorrect or invalid method');
407
-		// construct parameters
408
-		$principalUri = 'principals/user/attendee1';
409
-		$sender = '[email protected]';
410
-		$recipient = '[email protected]';
411
-		$calendar = $this->vCalendar1a;
412
-		// Act
413
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
414
-		// Assert
415
-		$this->assertFalse($result);
416
-	}
417
-
418
-	public function testHandleImipRequestWithInvalidMethod(): void {
419
-		// construct mock user calendar
420
-		$userCalendar = $this->createMock(ITestCalendar::class);
421
-		// construct mock calendar manager and returns
422
-		/** @var Manager&MockObject $manager */
423
-		$manager = $this->getMockBuilder(Manager::class)
424
-			->setConstructorArgs([
425
-				$this->coordinator,
426
-				$this->container,
427
-				$this->logger,
428
-				$this->time,
429
-				$this->secureRandom,
430
-				$this->userManager,
431
-				$this->serverFactory,
432
-			])
433
-			->onlyMethods(['getCalendarsForPrincipal'])
434
-			->getMock();
435
-		$manager->expects(self::once())
436
-			->method('getCalendarsForPrincipal')
437
-			->willReturn([$userCalendar]);
438
-		// construct logger returns
439
-		$this->logger->expects(self::once())->method('warning')
440
-			->with('iMip message contains an incorrect or invalid method');
441
-		// construct parameters
442
-		$principalUri = 'principals/user/attendee1';
443
-		$sender = '[email protected]';
444
-		$recipient = '[email protected]';
445
-		$calendar = $this->vCalendar1a;
446
-		$calendar->add('METHOD', 'CANCEL');
447
-		// Act
448
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
449
-		// Assert
450
-		$this->assertFalse($result);
451
-	}
452
-
453
-	public function testHandleImipRequestWithNoEvent(): void {
454
-		// construct mock user calendar
455
-		$userCalendar = $this->createMock(ITestCalendar::class);
456
-		// construct mock calendar manager and returns
457
-		/** @var Manager&MockObject $manager */
458
-		$manager = $this->getMockBuilder(Manager::class)
459
-			->setConstructorArgs([
460
-				$this->coordinator,
461
-				$this->container,
462
-				$this->logger,
463
-				$this->time,
464
-				$this->secureRandom,
465
-				$this->userManager,
466
-				$this->serverFactory,
467
-			])
468
-			->onlyMethods(['getCalendarsForPrincipal'])
469
-			->getMock();
470
-		$manager->expects(self::once())
471
-			->method('getCalendarsForPrincipal')
472
-			->willReturn([$userCalendar]);
473
-		// construct logger returns
474
-		$this->logger->expects(self::once())->method('warning')
475
-			->with('iMip message contains no event');
476
-		// construct parameters
477
-		$principalUri = 'principals/user/attendee1';
478
-		$sender = '[email protected]';
479
-		$recipient = '[email protected]';
480
-		$calendar = $this->vCalendar1a;
481
-		$calendar->add('METHOD', 'REQUEST');
482
-		$calendar->remove('VEVENT');
483
-		// Act
484
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
485
-		// Assert
486
-		$this->assertFalse($result);
487
-	}
488
-
489
-	public function testHandleImipRequestWithNoUid(): void {
490
-		// construct mock user calendar
491
-		$userCalendar = $this->createMock(ITestCalendar::class);
492
-		// construct mock calendar manager and returns
493
-		/** @var Manager&MockObject $manager */
494
-		$manager = $this->getMockBuilder(Manager::class)
495
-			->setConstructorArgs([
496
-				$this->coordinator,
497
-				$this->container,
498
-				$this->logger,
499
-				$this->time,
500
-				$this->secureRandom,
501
-				$this->userManager,
502
-				$this->serverFactory,
503
-			])
504
-			->onlyMethods(['getCalendarsForPrincipal'])
505
-			->getMock();
506
-		$manager->expects(self::once())
507
-			->method('getCalendarsForPrincipal')
508
-			->willReturn([$userCalendar]);
509
-		// construct logger returns
510
-		$this->logger->expects(self::once())->method('warning')
511
-			->with('iMip message event dose not contains a UID');
512
-		// construct parameters
513
-		$principalUri = 'principals/user/attendee1';
514
-		$sender = '[email protected]';
515
-		$recipient = '[email protected]';
516
-		$calendar = $this->vCalendar1a;
517
-		$calendar->add('METHOD', 'REQUEST');
518
-		$calendar->VEVENT->remove('UID');
519
-		// Act
520
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
521
-		// Assert
522
-		$this->assertFalse($result);
523
-	}
524
-
525
-	public function testHandleImipRequestWithNoOrganizer(): void {
526
-		// construct mock user calendar
527
-		$userCalendar = $this->createMock(ITestCalendar::class);
528
-		// construct mock calendar manager and returns
529
-		/** @var Manager&MockObject $manager */
530
-		$manager = $this->getMockBuilder(Manager::class)
531
-			->setConstructorArgs([
532
-				$this->coordinator,
533
-				$this->container,
534
-				$this->logger,
535
-				$this->time,
536
-				$this->secureRandom,
537
-				$this->userManager,
538
-				$this->serverFactory,
539
-			])
540
-			->onlyMethods(['getCalendarsForPrincipal'])
541
-			->getMock();
542
-		$manager->expects(self::once())
543
-			->method('getCalendarsForPrincipal')
544
-			->willReturn([$userCalendar]);
545
-		// construct logger returns
546
-		$this->logger->expects(self::once())->method('warning')
547
-			->with('iMip message event dose not contains an organizer');
548
-		// construct parameters
549
-		$principalUri = 'principals/user/attendee1';
550
-		$sender = '[email protected]';
551
-		$recipient = '[email protected]';
552
-		$calendar = $this->vCalendar1a;
553
-		$calendar->add('METHOD', 'REQUEST');
554
-		$calendar->VEVENT->remove('ORGANIZER');
555
-		// Act
556
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
557
-		// Assert
558
-		$this->assertFalse($result);
559
-	}
560
-
561
-	public function testHandleImipRequestWithNoAttendee(): void {
562
-		// construct mock user calendar
563
-		$userCalendar = $this->createMock(ITestCalendar::class);
564
-		// construct mock calendar manager and returns
565
-		/** @var Manager&MockObject $manager */
566
-		$manager = $this->getMockBuilder(Manager::class)
567
-			->setConstructorArgs([
568
-				$this->coordinator,
569
-				$this->container,
570
-				$this->logger,
571
-				$this->time,
572
-				$this->secureRandom,
573
-				$this->userManager,
574
-				$this->serverFactory,
575
-			])
576
-			->onlyMethods(['getCalendarsForPrincipal'])
577
-			->getMock();
578
-		$manager->expects(self::once())
579
-			->method('getCalendarsForPrincipal')
580
-			->willReturn([$userCalendar]);
581
-		// construct logger returns
582
-		$this->logger->expects(self::once())->method('warning')
583
-			->with('iMip message event dose not contains any attendees');
584
-		// construct parameters
585
-		$principalUri = 'principals/user/attendee1';
586
-		$sender = '[email protected]';
587
-		$recipient = '[email protected]';
588
-		$calendar = $this->vCalendar1a;
589
-		$calendar->add('METHOD', 'REQUEST');
590
-		$calendar->VEVENT->remove('ATTENDEE');
591
-		// Act
592
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
593
-		// Assert
594
-		$this->assertFalse($result);
595
-	}
596
-
597
-	public function testHandleImipRequestWithInvalidAttendee(): void {
598
-		// construct mock user calendar
599
-		$userCalendar = $this->createMock(ITestCalendar::class);
600
-		// construct mock calendar manager and returns
601
-		/** @var Manager&MockObject $manager */
602
-		$manager = $this->getMockBuilder(Manager::class)
603
-			->setConstructorArgs([
604
-				$this->coordinator,
605
-				$this->container,
606
-				$this->logger,
607
-				$this->time,
608
-				$this->secureRandom,
609
-				$this->userManager,
610
-				$this->serverFactory,
611
-			])
612
-			->onlyMethods(['getCalendarsForPrincipal'])
613
-			->getMock();
614
-		$manager->expects(self::once())
615
-			->method('getCalendarsForPrincipal')
616
-			->willReturn([$userCalendar]);
617
-		// construct logger returns
618
-		$this->logger->expects(self::once())->method('warning')
619
-			->with('iMip message event does not contain a attendee that matches the recipient');
620
-		// construct parameters
621
-		$principalUri = 'principals/user/attendee1';
622
-		$sender = '[email protected]';
623
-		$recipient = '[email protected]';
624
-		$calendar = $this->vCalendar1a;
625
-		$calendar->add('METHOD', 'REQUEST');
626
-		// Act
627
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
628
-		// Assert
629
-		$this->assertFalse($result);
630
-	}
631
-
632
-	public function testHandleImipRequestWithNoMatch(): void {
633
-		// construct mock user calendar
634
-		$userCalendar = $this->createMock(ITestCalendar::class);
635
-		$userCalendar->expects(self::once())
636
-			->method('isDeleted')
637
-			->willReturn(false);
638
-		$userCalendar->expects(self::once())
639
-			->method('isWritable')
640
-			->willReturn(true);
641
-		$userCalendar->expects(self::once())
642
-			->method('isShared')
643
-			->willReturn(false);
644
-		$userCalendar->expects(self::once())
645
-			->method('search')
646
-			->willReturn([]);
647
-		// construct mock calendar manager and returns
648
-		/** @var Manager&MockObject $manager */
649
-		$manager = $this->getMockBuilder(Manager::class)
650
-			->setConstructorArgs([
651
-				$this->coordinator,
652
-				$this->container,
653
-				$this->logger,
654
-				$this->time,
655
-				$this->secureRandom,
656
-				$this->userManager,
657
-				$this->serverFactory,
658
-			])
659
-			->onlyMethods(['getCalendarsForPrincipal'])
660
-			->getMock();
661
-		$manager->expects(self::once())
662
-			->method('getCalendarsForPrincipal')
663
-			->willReturn([$userCalendar]);
664
-		// construct logger returns
665
-		$this->logger->expects(self::once())->method('warning')
666
-			->with('iMip message event could not be processed because no corresponding event was found in any calendar');
667
-		// construct parameters
668
-		$principalUri = 'principals/user/attendee1';
669
-		$sender = '[email protected]';
670
-		$recipient = '[email protected]';
671
-		$calendar = $this->vCalendar1a;
672
-		$calendar->add('METHOD', 'REQUEST');
673
-		// Act
674
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
675
-		// Assert
676
-		$this->assertFalse($result);
677
-	}
678
-
679
-	public function testHandleImipRequest(): void {
680
-		// construct mock user calendar
681
-		$userCalendar = $this->createMock(ITestCalendar::class);
682
-		$userCalendar->expects(self::once())
683
-			->method('isDeleted')
684
-			->willReturn(false);
685
-		$userCalendar->expects(self::once())
686
-			->method('isWritable')
687
-			->willReturn(true);
688
-		$userCalendar->expects(self::once())
689
-			->method('isShared')
690
-			->willReturn(false);
691
-		$userCalendar->expects(self::once())
692
-			->method('search')
693
-			->willReturn([['uri' => 'principals/user/attendee1/personal']]);
694
-		// construct mock calendar manager and returns
695
-		/** @var Manager&MockObject $manager */
696
-		$manager = $this->getMockBuilder(Manager::class)
697
-			->setConstructorArgs([
698
-				$this->coordinator,
699
-				$this->container,
700
-				$this->logger,
701
-				$this->time,
702
-				$this->secureRandom,
703
-				$this->userManager,
704
-				$this->serverFactory,
705
-			])
706
-			->onlyMethods(['getCalendarsForPrincipal'])
707
-			->getMock();
708
-		$manager->expects(self::once())
709
-			->method('getCalendarsForPrincipal')
710
-			->willReturn([$userCalendar]);
711
-		// construct parameters
712
-		$principalUri = 'principals/user/attendee1';
713
-		$sender = '[email protected]';
714
-		$recipient = '[email protected]';
715
-		$calendar = $this->vCalendar1a;
716
-		$calendar->add('METHOD', 'REQUEST');
717
-		// construct user calendar returns
718
-		$userCalendar->expects(self::once())
719
-			->method('handleIMipMessage')
720
-			->with('', $calendar->serialize());
721
-		// Act
722
-		$result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
723
-		// Assert
724
-		$this->assertTrue($result);
725
-	}
726
-
727
-	public function testHandleImipReplyWithNoCalendars(): void {
728
-		// construct calendar manager returns
729
-		/** @var Manager&MockObject $manager */
730
-		$manager = $this->getMockBuilder(Manager::class)
731
-			->setConstructorArgs([
732
-				$this->coordinator,
733
-				$this->container,
734
-				$this->logger,
735
-				$this->time,
736
-				$this->secureRandom,
737
-				$this->userManager,
738
-				$this->serverFactory,
739
-			])
740
-			->onlyMethods(['getCalendarsForPrincipal'])
741
-			->getMock();
742
-		$manager->expects(self::once())
743
-			->method('getCalendarsForPrincipal')
744
-			->willReturn([]);
745
-		// construct logger returns
746
-		$this->logger->expects(self::once())->method('warning')
747
-			->with('iMip message could not be processed because user has no calendars');
748
-		// construct parameters
749
-		$principalUri = 'principals/user/linus';
750
-		$sender = '[email protected]';
751
-		$recipient = '[email protected]';
752
-		$calendar = $this->vCalendar2a;
753
-		$calendar->add('METHOD', 'REPLY');
754
-		// Act
755
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
756
-		// Assert
757
-		$this->assertFalse($result);
758
-	}
759
-
760
-	public function testHandleImipReplyWithInvalidData(): void {
761
-		// construct mock user calendar
762
-		$userCalendar = $this->createMock(ITestCalendar::class);
763
-		// construct mock calendar manager and returns
764
-		/** @var Manager&MockObject $manager */
765
-		$manager = $this->getMockBuilder(Manager::class)
766
-			->setConstructorArgs([
767
-				$this->coordinator,
768
-				$this->container,
769
-				$this->logger,
770
-				$this->time,
771
-				$this->secureRandom,
772
-				$this->userManager,
773
-				$this->serverFactory,
774
-			])
775
-			->onlyMethods(['getCalendarsForPrincipal'])
776
-			->getMock();
777
-		$manager->expects(self::once())
778
-			->method('getCalendarsForPrincipal')
779
-			->willReturn([$userCalendar]);
780
-		// construct logger returns
781
-		$this->logger->expects(self::once())->method('error')
782
-			->with('iMip message could not be processed because an error occurred while parsing the iMip message');
783
-		// construct parameters
784
-		$principalUri = 'principals/user/attendee1';
785
-		$sender = '[email protected]';
786
-		$recipient = '[email protected]';
787
-		// Act
788
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, 'Invalid data');
789
-		// Assert
790
-		$this->assertFalse($result);
791
-	}
792
-
793
-	public function testHandleImipReplyWithNoMethod(): void {
794
-		// construct mock user calendar
795
-		$userCalendar = $this->createMock(ITestCalendar::class);
796
-		// construct mock calendar manager and returns
797
-		/** @var Manager&MockObject $manager */
798
-		$manager = $this->getMockBuilder(Manager::class)
799
-			->setConstructorArgs([
800
-				$this->coordinator,
801
-				$this->container,
802
-				$this->logger,
803
-				$this->time,
804
-				$this->secureRandom,
805
-				$this->userManager,
806
-				$this->serverFactory,
807
-			])
808
-			->onlyMethods(['getCalendarsForPrincipal'])
809
-			->getMock();
810
-		$manager->expects(self::once())
811
-			->method('getCalendarsForPrincipal')
812
-			->willReturn([$userCalendar]);
813
-		// construct logger returns
814
-		$this->logger->expects(self::once())->method('warning')
815
-			->with('iMip message contains an incorrect or invalid method');
816
-		// construct parameters
817
-		$principalUri = 'principals/user/linus';
818
-		$sender = '[email protected]';
819
-		$recipient = '[email protected]';
820
-		$calendar = $this->vCalendar2a;
821
-		// Act
822
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
823
-		// Assert
824
-		$this->assertFalse($result);
825
-	}
826
-
827
-	public function testHandleImipReplyWithInvalidMethod(): void {
828
-		// construct mock user calendar
829
-		$userCalendar = $this->createMock(ITestCalendar::class);
830
-		// construct mock calendar manager and returns
831
-		/** @var Manager&MockObject $manager */
832
-		$manager = $this->getMockBuilder(Manager::class)
833
-			->setConstructorArgs([
834
-				$this->coordinator,
835
-				$this->container,
836
-				$this->logger,
837
-				$this->time,
838
-				$this->secureRandom,
839
-				$this->userManager,
840
-				$this->serverFactory,
841
-			])
842
-			->onlyMethods(['getCalendarsForPrincipal'])
843
-			->getMock();
844
-		$manager->expects(self::once())
845
-			->method('getCalendarsForPrincipal')
846
-			->willReturn([$userCalendar]);
847
-		// construct logger returns
848
-		$this->logger->expects(self::once())->method('warning')
849
-			->with('iMip message contains an incorrect or invalid method');
850
-		// construct parameters
851
-		$principalUri = 'principals/user/linus';
852
-		$sender = '[email protected]';
853
-		$recipient = '[email protected]';
854
-		$calendar = $this->vCalendar2a;
855
-		$calendar->add('METHOD', 'UNKNOWN');
856
-		// Act
857
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
858
-		// Assert
859
-		$this->assertFalse($result);
860
-	}
861
-
862
-	public function testHandleImipReplyWithNoEvent(): void {
863
-		// construct mock user calendar
864
-		$userCalendar = $this->createMock(ITestCalendar::class);
865
-		// construct mock calendar manager and returns
866
-		/** @var Manager&MockObject $manager */
867
-		$manager = $this->getMockBuilder(Manager::class)
868
-			->setConstructorArgs([
869
-				$this->coordinator,
870
-				$this->container,
871
-				$this->logger,
872
-				$this->time,
873
-				$this->secureRandom,
874
-				$this->userManager,
875
-				$this->serverFactory,
876
-			])
877
-			->onlyMethods(['getCalendarsForPrincipal'])
878
-			->getMock();
879
-		$manager->expects(self::once())
880
-			->method('getCalendarsForPrincipal')
881
-			->willReturn([$userCalendar]);
882
-		// construct logger returns
883
-		$this->logger->expects(self::once())->method('warning')
884
-			->with('iMip message contains no event');
885
-		// construct parameters
886
-		$principalUri = 'principals/user/linus';
887
-		$sender = '[email protected]';
888
-		$recipient = '[email protected]';
889
-		$calendar = $this->vCalendar2a;
890
-		$calendar->add('METHOD', 'REPLY');
891
-		$calendar->remove('VEVENT');
892
-		// Act
893
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
894
-		// Assert
895
-		$this->assertFalse($result);
896
-	}
897
-
898
-	public function testHandleImipReplyWithNoUid(): void {
899
-		// construct mock user calendar
900
-		$userCalendar = $this->createMock(ITestCalendar::class);
901
-		// construct mock calendar manager and returns
902
-		/** @var Manager&MockObject $manager */
903
-		$manager = $this->getMockBuilder(Manager::class)
904
-			->setConstructorArgs([
905
-				$this->coordinator,
906
-				$this->container,
907
-				$this->logger,
908
-				$this->time,
909
-				$this->secureRandom,
910
-				$this->userManager,
911
-				$this->serverFactory,
912
-			])
913
-			->onlyMethods(['getCalendarsForPrincipal'])
914
-			->getMock();
915
-		$manager->expects(self::once())
916
-			->method('getCalendarsForPrincipal')
917
-			->willReturn([$userCalendar]);
918
-		// construct logger returns
919
-		$this->logger->expects(self::once())->method('warning')
920
-			->with('iMip message event dose not contains a UID');
921
-		// construct parameters
922
-		$principalUri = 'principals/user/linus';
923
-		$sender = '[email protected]';
924
-		$recipient = '[email protected]';
925
-		$calendar = $this->vCalendar2a;
926
-		$calendar->add('METHOD', 'REPLY');
927
-		$calendar->VEVENT->remove('UID');
928
-		// Act
929
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
930
-		// Assert
931
-		$this->assertFalse($result);
932
-	}
933
-
934
-	public function testHandleImipReplyWithNoOrganizer(): void {
935
-		// construct mock user calendar
936
-		$userCalendar = $this->createMock(ITestCalendar::class);
937
-		// construct mock calendar manager and returns
938
-		/** @var Manager&MockObject $manager */
939
-		$manager = $this->getMockBuilder(Manager::class)
940
-			->setConstructorArgs([
941
-				$this->coordinator,
942
-				$this->container,
943
-				$this->logger,
944
-				$this->time,
945
-				$this->secureRandom,
946
-				$this->userManager,
947
-				$this->serverFactory,
948
-			])
949
-			->onlyMethods(['getCalendarsForPrincipal'])
950
-			->getMock();
951
-		$manager->expects(self::once())
952
-			->method('getCalendarsForPrincipal')
953
-			->willReturn([$userCalendar]);
954
-		// construct logger returns
955
-		$this->logger->expects(self::once())->method('warning')
956
-			->with('iMip message event dose not contains an organizer');
957
-		// construct parameters
958
-		$principalUri = 'principals/user/linus';
959
-		$sender = '[email protected]';
960
-		$recipient = '[email protected]';
961
-		$calendar = $this->vCalendar2a;
962
-		$calendar->add('METHOD', 'REPLY');
963
-		$calendar->VEVENT->remove('ORGANIZER');
964
-		// Act
965
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
966
-		// Assert
967
-		$this->assertFalse($result);
968
-	}
969
-
970
-	public function testHandleImipReplyWithNoAttendee(): void {
971
-		// construct mock user calendar
972
-		$userCalendar = $this->createMock(ITestCalendar::class);
973
-		// construct mock calendar manager and returns
974
-		/** @var Manager&MockObject $manager */
975
-		$manager = $this->getMockBuilder(Manager::class)
976
-			->setConstructorArgs([
977
-				$this->coordinator,
978
-				$this->container,
979
-				$this->logger,
980
-				$this->time,
981
-				$this->secureRandom,
982
-				$this->userManager,
983
-				$this->serverFactory,
984
-			])
985
-			->onlyMethods(['getCalendarsForPrincipal'])
986
-			->getMock();
987
-		$manager->expects(self::once())
988
-			->method('getCalendarsForPrincipal')
989
-			->willReturn([$userCalendar]);
990
-		// construct logger returns
991
-		$this->logger->expects(self::once())->method('warning')
992
-			->with('iMip message event dose not contains any attendees');
993
-		// construct parameters
994
-		$principalUri = 'principals/user/linus';
995
-		$sender = '[email protected]';
996
-		$recipient = '[email protected]';
997
-		$calendar = $this->vCalendar2a;
998
-		$calendar->add('METHOD', 'REPLY');
999
-		$calendar->VEVENT->remove('ATTENDEE');
1000
-		// Act
1001
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
1002
-		// Assert
1003
-		$this->assertFalse($result);
1004
-	}
1005
-
1006
-	public function testHandleImipReplyDateInThePast(): void {
1007
-		// construct mock user calendar
1008
-		$userCalendar = $this->createMock(ITestCalendar::class);
1009
-		// construct mock calendar manager and returns
1010
-		/** @var Manager&MockObject $manager */
1011
-		$manager = $this->getMockBuilder(Manager::class)
1012
-			->setConstructorArgs([
1013
-				$this->coordinator,
1014
-				$this->container,
1015
-				$this->logger,
1016
-				$this->time,
1017
-				$this->secureRandom,
1018
-				$this->userManager,
1019
-				$this->serverFactory,
1020
-			])
1021
-			->onlyMethods(['getCalendarsForPrincipal'])
1022
-			->getMock();
1023
-		$manager->expects(self::once())
1024
-			->method('getCalendarsForPrincipal')
1025
-			->willReturn([$userCalendar]);
1026
-		// construct logger and time returns
1027
-		$this->logger->expects(self::once())->method('warning')
1028
-			->with('iMip message event could not be processed because the event is in the past');
1029
-		$this->time->expects(self::once())
1030
-			->method('getTime')
1031
-			->willReturn(time());
1032
-		// construct parameters
1033
-		$principalUri = 'principals/user/linus';
1034
-		$sender = '[email protected]';
1035
-		$recipient = '[email protected]';
1036
-		$calendarData = clone $this->vCalendar2a;
1037
-		$calendarData->add('METHOD', 'REPLY');
1038
-		$calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
1039
-		// Act
1040
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1041
-		// Assert
1042
-		$this->assertFalse($result);
1043
-	}
1044
-
1045
-	public function testHandleImipReplyEventNotFound(): void {
1046
-		// construct mock user calendar
1047
-		$userCalendar = $this->createMock(ITestCalendar::class);
1048
-		$userCalendar->expects(self::once())
1049
-			->method('search')
1050
-			->willReturn([]);
1051
-		// construct mock calendar manager and returns
1052
-		/** @var Manager&MockObject $manager */
1053
-		$manager = $this->getMockBuilder(Manager::class)
1054
-			->setConstructorArgs([
1055
-				$this->coordinator,
1056
-				$this->container,
1057
-				$this->logger,
1058
-				$this->time,
1059
-				$this->secureRandom,
1060
-				$this->userManager,
1061
-				$this->serverFactory,
1062
-			])
1063
-			->onlyMethods(['getCalendarsForPrincipal'])
1064
-			->getMock();
1065
-		$manager->expects(self::once())
1066
-			->method('getCalendarsForPrincipal')
1067
-			->willReturn([$userCalendar]);
1068
-		// construct time returns
1069
-		$this->time->expects(self::once())
1070
-			->method('getTime')
1071
-			->willReturn(1628374233);
1072
-		// construct parameters
1073
-		$principalUri = 'principals/user/linus';
1074
-		$sender = '[email protected]';
1075
-		$recipient = '[email protected]';
1076
-		$calendarData = clone $this->vCalendar2a;
1077
-		$calendarData->add('METHOD', 'REPLY');
1078
-		// construct logger return
1079
-		$this->logger->expects(self::once())->method('warning')
1080
-			->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1081
-		// Act
1082
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1083
-		// Assert
1084
-		$this->assertFalse($result);
1085
-	}
1086
-
1087
-	public function testHandleImipReply(): void {
1088
-		/** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1089
-		$manager = $this->getMockBuilder(Manager::class)
1090
-			->setConstructorArgs([
1091
-				$this->coordinator,
1092
-				$this->container,
1093
-				$this->logger,
1094
-				$this->time,
1095
-				$this->secureRandom,
1096
-				$this->userManager,
1097
-				$this->serverFactory,
1098
-			])
1099
-			->setMethods([
1100
-				'getCalendarsForPrincipal'
1101
-			])
1102
-			->getMock();
1103
-		$calendar = $this->createMock(ITestCalendar::class);
1104
-		$principalUri = 'principals/user/linus';
1105
-		$sender = '[email protected]';
1106
-		$recipient = '[email protected]';
1107
-		$calendarData = clone $this->vCalendar2a;
1108
-		$calendarData->add('METHOD', 'REPLY');
1109
-
1110
-		$this->time->expects(self::once())
1111
-			->method('getTime')
1112
-			->willReturn(1628374233);
1113
-		$manager->expects(self::once())
1114
-			->method('getCalendarsForPrincipal')
1115
-			->willReturn([$calendar]);
1116
-		$calendar->expects(self::once())
1117
-			->method('search')
1118
-			->willReturn([['uri' => 'testname.ics']]);
1119
-		$calendar->expects(self::once())
1120
-			->method('handleIMipMessage')
1121
-			->with('testname.ics', $calendarData->serialize());
1122
-		// Act
1123
-		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1124
-		// Assert
1125
-		$this->assertTrue($result);
1126
-	}
1127
-
1128
-	public function testHandleImipCancelWithNoCalendars(): void {
1129
-		// construct calendar manager returns
1130
-		/** @var Manager&MockObject $manager */
1131
-		$manager = $this->getMockBuilder(Manager::class)
1132
-			->setConstructorArgs([
1133
-				$this->coordinator,
1134
-				$this->container,
1135
-				$this->logger,
1136
-				$this->time,
1137
-				$this->secureRandom,
1138
-				$this->userManager,
1139
-				$this->serverFactory,
1140
-			])
1141
-			->onlyMethods(['getCalendarsForPrincipal'])
1142
-			->getMock();
1143
-		$manager->expects(self::once())
1144
-			->method('getCalendarsForPrincipal')
1145
-			->willReturn([]);
1146
-		// construct logger returns
1147
-		$this->logger->expects(self::once())->method('warning')
1148
-			->with('iMip message could not be processed because user has no calendars');
1149
-		// construct parameters
1150
-		$principalUri = 'principals/user/pierre';
1151
-		$sender = '[email protected]';
1152
-		$recipient = '[email protected]';
1153
-		$replyTo = null;
1154
-		$calendar = $this->vCalendar3a;
1155
-		$calendar->add('METHOD', 'CANCEL');
1156
-		// Act
1157
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1158
-		// Assert
1159
-		$this->assertFalse($result);
1160
-	}
1161
-
1162
-	public function testHandleImipCancelWithInvalidData(): void {
1163
-		// construct mock user calendar
1164
-		$userCalendar = $this->createMock(ITestCalendar::class);
1165
-		// construct mock calendar manager and returns
1166
-		/** @var Manager&MockObject $manager */
1167
-		$manager = $this->getMockBuilder(Manager::class)
1168
-			->setConstructorArgs([
1169
-				$this->coordinator,
1170
-				$this->container,
1171
-				$this->logger,
1172
-				$this->time,
1173
-				$this->secureRandom,
1174
-				$this->userManager,
1175
-				$this->serverFactory,
1176
-			])
1177
-			->onlyMethods(['getCalendarsForPrincipal'])
1178
-			->getMock();
1179
-		$manager->expects(self::once())
1180
-			->method('getCalendarsForPrincipal')
1181
-			->willReturn([$userCalendar]);
1182
-		// construct logger returns
1183
-		$this->logger->expects(self::once())->method('error')
1184
-			->with('iMip message could not be processed because an error occurred while parsing the iMip message');
1185
-		// construct parameters
1186
-		$principalUri = 'principals/user/attendee1';
1187
-		$sender = '[email protected]';
1188
-		$recipient = '[email protected]';
1189
-		$replyTo = null;
1190
-		// Act
1191
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, 'Invalid data');
1192
-		// Assert
1193
-		$this->assertFalse($result);
1194
-	}
1195
-
1196
-
1197
-	public function testHandleImipCancelWithNoMethod(): void {
1198
-		// construct mock user calendar
1199
-		$userCalendar = $this->createMock(ITestCalendar::class);
1200
-		// construct mock calendar manager and returns
1201
-		/** @var Manager&MockObject $manager */
1202
-		$manager = $this->getMockBuilder(Manager::class)
1203
-			->setConstructorArgs([
1204
-				$this->coordinator,
1205
-				$this->container,
1206
-				$this->logger,
1207
-				$this->time,
1208
-				$this->secureRandom,
1209
-				$this->userManager,
1210
-				$this->serverFactory,
1211
-			])
1212
-			->onlyMethods(['getCalendarsForPrincipal'])
1213
-			->getMock();
1214
-		$manager->expects(self::once())
1215
-			->method('getCalendarsForPrincipal')
1216
-			->willReturn([$userCalendar]);
1217
-		// construct logger returns
1218
-		$this->logger->expects(self::once())->method('warning')
1219
-			->with('iMip message contains an incorrect or invalid method');
1220
-		// construct parameters
1221
-		$principalUri = 'principals/user/pierre';
1222
-		$sender = '[email protected]';
1223
-		$recipient = '[email protected]';
1224
-		$replyTo = null;
1225
-		$calendar = $this->vCalendar3a;
1226
-		// Act
1227
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1228
-		// Assert
1229
-		$this->assertFalse($result);
1230
-	}
1231
-
1232
-	public function testHandleImipCancelWithInvalidMethod(): void {
1233
-		// construct mock user calendar
1234
-		$userCalendar = $this->createMock(ITestCalendar::class);
1235
-		// construct mock calendar manager and returns
1236
-		/** @var Manager&MockObject $manager */
1237
-		$manager = $this->getMockBuilder(Manager::class)
1238
-			->setConstructorArgs([
1239
-				$this->coordinator,
1240
-				$this->container,
1241
-				$this->logger,
1242
-				$this->time,
1243
-				$this->secureRandom,
1244
-				$this->userManager,
1245
-				$this->serverFactory,
1246
-			])
1247
-			->onlyMethods(['getCalendarsForPrincipal'])
1248
-			->getMock();
1249
-		$manager->expects(self::once())
1250
-			->method('getCalendarsForPrincipal')
1251
-			->willReturn([$userCalendar]);
1252
-		// construct logger returns
1253
-		$this->logger->expects(self::once())->method('warning')
1254
-			->with('iMip message contains an incorrect or invalid method');
1255
-		// construct parameters
1256
-		$principalUri = 'principals/user/pierre';
1257
-		$sender = '[email protected]';
1258
-		$recipient = '[email protected]';
1259
-		$replyTo = null;
1260
-		$calendar = $this->vCalendar3a;
1261
-		$calendar->add('METHOD', 'UNKNOWN');
1262
-		// Act
1263
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1264
-		// Assert
1265
-		$this->assertFalse($result);
1266
-	}
1267
-
1268
-	public function testHandleImipCancelWithNoEvent(): void {
1269
-		// construct mock user calendar
1270
-		$userCalendar = $this->createMock(ITestCalendar::class);
1271
-		// construct mock calendar manager and returns
1272
-		/** @var Manager&MockObject $manager */
1273
-		$manager = $this->getMockBuilder(Manager::class)
1274
-			->setConstructorArgs([
1275
-				$this->coordinator,
1276
-				$this->container,
1277
-				$this->logger,
1278
-				$this->time,
1279
-				$this->secureRandom,
1280
-				$this->userManager,
1281
-				$this->serverFactory,
1282
-			])
1283
-			->onlyMethods(['getCalendarsForPrincipal'])
1284
-			->getMock();
1285
-		$manager->expects(self::once())
1286
-			->method('getCalendarsForPrincipal')
1287
-			->willReturn([$userCalendar]);
1288
-		// construct logger returns
1289
-		$this->logger->expects(self::once())->method('warning')
1290
-			->with('iMip message contains no event');
1291
-		// construct parameters
1292
-		$principalUri = 'principals/user/pierre';
1293
-		$sender = '[email protected]';
1294
-		$recipient = '[email protected]';
1295
-		$replyTo = null;
1296
-		$calendar = $this->vCalendar3a;
1297
-		$calendar->add('METHOD', 'CANCEL');
1298
-		$calendar->remove('VEVENT');
1299
-		// Act
1300
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1301
-		// Assert
1302
-		$this->assertFalse($result);
1303
-	}
1304
-
1305
-	public function testHandleImipCancelWithNoUid(): void {
1306
-		// construct mock user calendar
1307
-		$userCalendar = $this->createMock(ITestCalendar::class);
1308
-		// construct mock calendar manager and returns
1309
-		/** @var Manager&MockObject $manager */
1310
-		$manager = $this->getMockBuilder(Manager::class)
1311
-			->setConstructorArgs([
1312
-				$this->coordinator,
1313
-				$this->container,
1314
-				$this->logger,
1315
-				$this->time,
1316
-				$this->secureRandom,
1317
-				$this->userManager,
1318
-				$this->serverFactory,
1319
-			])
1320
-			->onlyMethods(['getCalendarsForPrincipal'])
1321
-			->getMock();
1322
-		$manager->expects(self::once())
1323
-			->method('getCalendarsForPrincipal')
1324
-			->willReturn([$userCalendar]);
1325
-		// construct logger returns
1326
-		$this->logger->expects(self::once())->method('warning')
1327
-			->with('iMip message event dose not contains a UID');
1328
-		// construct parameters
1329
-		$principalUri = 'principals/user/pierre';
1330
-		$sender = '[email protected]';
1331
-		$recipient = '[email protected]';
1332
-		$replyTo = null;
1333
-		$calendar = $this->vCalendar3a;
1334
-		$calendar->add('METHOD', 'CANCEL');
1335
-		$calendar->VEVENT->remove('UID');
1336
-		// Act
1337
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1338
-		// Assert
1339
-		$this->assertFalse($result);
1340
-	}
1341
-
1342
-	public function testHandleImipCancelWithNoOrganizer(): void {
1343
-		// construct mock user calendar
1344
-		$userCalendar = $this->createMock(ITestCalendar::class);
1345
-		// construct mock calendar manager and returns
1346
-		/** @var Manager&MockObject $manager */
1347
-		$manager = $this->getMockBuilder(Manager::class)
1348
-			->setConstructorArgs([
1349
-				$this->coordinator,
1350
-				$this->container,
1351
-				$this->logger,
1352
-				$this->time,
1353
-				$this->secureRandom,
1354
-				$this->userManager,
1355
-				$this->serverFactory,
1356
-			])
1357
-			->onlyMethods(['getCalendarsForPrincipal'])
1358
-			->getMock();
1359
-		$manager->expects(self::once())
1360
-			->method('getCalendarsForPrincipal')
1361
-			->willReturn([$userCalendar]);
1362
-		// construct logger returns
1363
-		$this->logger->expects(self::once())->method('warning')
1364
-			->with('iMip message event dose not contains an organizer');
1365
-		// construct parameters
1366
-		$principalUri = 'principals/user/pierre';
1367
-		$sender = '[email protected]';
1368
-		$recipient = '[email protected]';
1369
-		$replyTo = null;
1370
-		$calendar = $this->vCalendar3a;
1371
-		$calendar->add('METHOD', 'CANCEL');
1372
-		$calendar->VEVENT->remove('ORGANIZER');
1373
-		// Act
1374
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1375
-		// Assert
1376
-		$this->assertFalse($result);
1377
-	}
1378
-
1379
-	public function testHandleImipCancelWithNoAttendee(): void {
1380
-		// construct mock user calendar
1381
-		$userCalendar = $this->createMock(ITestCalendar::class);
1382
-		// construct mock calendar manager and returns
1383
-		/** @var Manager&MockObject $manager */
1384
-		$manager = $this->getMockBuilder(Manager::class)
1385
-			->setConstructorArgs([
1386
-				$this->coordinator,
1387
-				$this->container,
1388
-				$this->logger,
1389
-				$this->time,
1390
-				$this->secureRandom,
1391
-				$this->userManager,
1392
-				$this->serverFactory,
1393
-			])
1394
-			->onlyMethods(['getCalendarsForPrincipal'])
1395
-			->getMock();
1396
-		$manager->expects(self::once())
1397
-			->method('getCalendarsForPrincipal')
1398
-			->willReturn([$userCalendar]);
1399
-		// construct logger returns
1400
-		$this->logger->expects(self::once())->method('warning')
1401
-			->with('iMip message event dose not contains any attendees');
1402
-		// construct parameters
1403
-		$principalUri = 'principals/user/pierre';
1404
-		$sender = '[email protected]';
1405
-		$recipient = '[email protected]';
1406
-		$replyTo = null;
1407
-		$calendar = $this->vCalendar3a;
1408
-		$calendar->add('METHOD', 'CANCEL');
1409
-		$calendar->VEVENT->remove('ATTENDEE');
1410
-		// Act
1411
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1412
-		// Assert
1413
-		$this->assertFalse($result);
1414
-	}
1415
-
1416
-	public function testHandleImipCancelAttendeeNotRecipient(): void {
1417
-		// construct mock user calendar
1418
-		$userCalendar = $this->createMock(ITestCalendar::class);
1419
-		// construct mock calendar manager and returns
1420
-		/** @var Manager&MockObject $manager */
1421
-		$manager = $this->getMockBuilder(Manager::class)
1422
-			->setConstructorArgs([
1423
-				$this->coordinator,
1424
-				$this->container,
1425
-				$this->logger,
1426
-				$this->time,
1427
-				$this->secureRandom,
1428
-				$this->userManager,
1429
-				$this->serverFactory,
1430
-			])
1431
-			->onlyMethods(['getCalendarsForPrincipal'])
1432
-			->getMock();
1433
-		$manager->expects(self::once())
1434
-			->method('getCalendarsForPrincipal')
1435
-			->willReturn([$userCalendar]);
1436
-		// construct logger returns
1437
-		$this->logger->expects(self::once())->method('warning')
1438
-			->with('iMip message event could not be processed because recipient must be an ATTENDEE of this event');
1439
-		// construct parameters
1440
-		$principalUri = 'principals/user/pierre';
1441
-		$sender = '[email protected]';
1442
-		$recipient = '[email protected]';
1443
-		$replyTo = null;
1444
-		$calendarData = clone $this->vCalendar3a;
1445
-		$calendarData->add('METHOD', 'CANCEL');
1446
-		// Act
1447
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1448
-		// Assert
1449
-		$this->assertFalse($result);
1450
-	}
1451
-
1452
-	public function testHandleImipCancelDateInThePast(): void {
1453
-		// construct mock user calendar
1454
-		$userCalendar = $this->createMock(ITestCalendar::class);
1455
-		// construct mock calendar manager and returns
1456
-		/** @var Manager&MockObject $manager */
1457
-		$manager = $this->getMockBuilder(Manager::class)
1458
-			->setConstructorArgs([
1459
-				$this->coordinator,
1460
-				$this->container,
1461
-				$this->logger,
1462
-				$this->time,
1463
-				$this->secureRandom,
1464
-				$this->userManager,
1465
-				$this->serverFactory,
1466
-			])
1467
-			->onlyMethods(['getCalendarsForPrincipal'])
1468
-			->getMock();
1469
-		$manager->expects(self::once())
1470
-			->method('getCalendarsForPrincipal')
1471
-			->willReturn([$userCalendar]);
1472
-		// construct logger and time returns
1473
-		$this->logger->expects(self::once())->method('warning')
1474
-			->with('iMip message event could not be processed because the event is in the past');
1475
-		$this->time->expects(self::once())
1476
-			->method('getTime')
1477
-			->willReturn(time());
1478
-		// construct parameters
1479
-		$principalUri = 'principals/user/pierre';
1480
-		$sender = '[email protected]';
1481
-		$recipient = '[email protected]';
1482
-		$replyTo = null;
1483
-		$calendarData = clone $this->vCalendar3a;
1484
-		$calendarData->add('METHOD', 'CANCEL');
1485
-		$calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
1486
-		// Act
1487
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1488
-		// Assert
1489
-		$this->assertFalse($result);
1490
-	}
1491
-
1492
-	public function testHandleImipCancelEventNotFound(): void {
1493
-		// construct mock user calendar
1494
-		$userCalendar = $this->createMock(ITestCalendar::class);
1495
-		$userCalendar->expects(self::once())
1496
-			->method('search')
1497
-			->willReturn([]);
1498
-		// construct mock calendar manager and returns
1499
-		/** @var Manager&MockObject $manager */
1500
-		$manager = $this->getMockBuilder(Manager::class)
1501
-			->setConstructorArgs([
1502
-				$this->coordinator,
1503
-				$this->container,
1504
-				$this->logger,
1505
-				$this->time,
1506
-				$this->secureRandom,
1507
-				$this->userManager,
1508
-				$this->serverFactory,
1509
-			])
1510
-			->onlyMethods(['getCalendarsForPrincipal'])
1511
-			->getMock();
1512
-		$manager->expects(self::once())
1513
-			->method('getCalendarsForPrincipal')
1514
-			->willReturn([$userCalendar]);
1515
-		// construct time returns
1516
-		$this->time->expects(self::once())
1517
-			->method('getTime')
1518
-			->willReturn(1628374233);
1519
-		// construct parameters
1520
-		$principalUri = 'principals/user/pierre';
1521
-		$sender = '[email protected]';
1522
-		$recipient = '[email protected]';
1523
-		$replyTo = null;
1524
-		$calendarData = clone $this->vCalendar3a;
1525
-		$calendarData->add('METHOD', 'CANCEL');
1526
-		// construct logger return
1527
-		$this->logger->expects(self::once())->method('warning')
1528
-			->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1529
-		// Act
1530
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1531
-		// Assert
1532
-		$this->assertFalse($result);
1533
-	}
1534
-
1535
-	public function testHandleImipCancelOrganiserInReplyTo(): void {
1536
-		/** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1537
-		$manager = $this->getMockBuilder(Manager::class)
1538
-			->setConstructorArgs([
1539
-				$this->coordinator,
1540
-				$this->container,
1541
-				$this->logger,
1542
-				$this->time,
1543
-				$this->secureRandom,
1544
-				$this->userManager,
1545
-				$this->serverFactory,
1546
-			])
1547
-			->setMethods([
1548
-				'getCalendarsForPrincipal'
1549
-			])
1550
-			->getMock();
39
+    /** @var Coordinator&MockObject */
40
+    private $coordinator;
41
+
42
+    /** @var ContainerInterface&MockObject */
43
+    private $container;
44
+
45
+    /** @var LoggerInterface&MockObject */
46
+    private $logger;
47
+
48
+    /** @var Manager */
49
+    private $manager;
50
+
51
+    /** @var ITimeFactory&MockObject */
52
+    private $time;
53
+
54
+    /** @var ISecureRandom&MockObject */
55
+    private ISecureRandom $secureRandom;
56
+
57
+    private IUserManager&MockObject $userManager;
58
+    private ServerFactory&MockObject $serverFactory;
59
+
60
+    private VCalendar $vCalendar1a;
61
+    private VCalendar $vCalendar2a;
62
+    private VCalendar $vCalendar3a;
63
+
64
+    protected function setUp(): void {
65
+        parent::setUp();
66
+
67
+        $this->coordinator = $this->createMock(Coordinator::class);
68
+        $this->container = $this->createMock(ContainerInterface::class);
69
+        $this->logger = $this->createMock(LoggerInterface::class);
70
+        $this->time = $this->createMock(ITimeFactory::class);
71
+        $this->secureRandom = $this->createMock(ISecureRandom::class);
72
+        $this->userManager = $this->createMock(IUserManager::class);
73
+        $this->serverFactory = $this->createMock(ServerFactory::class);
74
+
75
+        $this->manager = new Manager(
76
+            $this->coordinator,
77
+            $this->container,
78
+            $this->logger,
79
+            $this->time,
80
+            $this->secureRandom,
81
+            $this->userManager,
82
+            $this->serverFactory,
83
+        );
84
+
85
+        // construct calendar with a 1 hour event and same start/end time zones
86
+        $this->vCalendar1a = new VCalendar();
87
+        /** @var VEvent $vEvent */
88
+        $vEvent = $this->vCalendar1a->add('VEVENT', []);
89
+        $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
90
+        $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
91
+        $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
92
+        $vEvent->add('SUMMARY', 'Test Event');
93
+        $vEvent->add('SEQUENCE', 3);
94
+        $vEvent->add('STATUS', 'CONFIRMED');
95
+        $vEvent->add('TRANSP', 'OPAQUE');
96
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'Organizer']);
97
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
98
+            'CN' => 'Attendee One',
99
+            'CUTYPE' => 'INDIVIDUAL',
100
+            'PARTSTAT' => 'NEEDS-ACTION',
101
+            'ROLE' => 'REQ-PARTICIPANT',
102
+            'RSVP' => 'TRUE'
103
+        ]);
104
+
105
+        // construct calendar with a event for reply
106
+        $this->vCalendar2a = new VCalendar();
107
+        /** @var VEvent $vEvent */
108
+        $vEvent = $this->vCalendar2a->add('VEVENT', []);
109
+        $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
110
+        $vEvent->add('DTSTART', '20210820');
111
+        $vEvent->add('DTEND', '20220821');
112
+        $vEvent->add('SUMMARY', 'berry basket');
113
+        $vEvent->add('SEQUENCE', 3);
114
+        $vEvent->add('STATUS', 'CONFIRMED');
115
+        $vEvent->add('TRANSP', 'OPAQUE');
116
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
117
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
118
+            'CN' => '[email protected]',
119
+            'CUTYPE' => 'INDIVIDUAL',
120
+            'ROLE' => 'REQ-PARTICIPANT',
121
+            'PARTSTAT' => 'ACCEPTED',
122
+        ]);
123
+
124
+        // construct calendar with a event for reply
125
+        $this->vCalendar3a = new VCalendar();
126
+        /** @var VEvent $vEvent */
127
+        $vEvent = $this->vCalendar3a->add('VEVENT', []);
128
+        $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
129
+        $vEvent->add('DTSTART', '20210820');
130
+        $vEvent->add('DTEND', '20220821');
131
+        $vEvent->add('SUMMARY', 'berry basket');
132
+        $vEvent->add('SEQUENCE', 3);
133
+        $vEvent->add('STATUS', 'CANCELLED');
134
+        $vEvent->add('TRANSP', 'OPAQUE');
135
+        $vEvent->add('ORGANIZER', 'mailto:[email protected]', ['CN' => 'admin']);
136
+        $vEvent->add('ATTENDEE', 'mailto:[email protected]', [
137
+            'CN' => '[email protected]',
138
+            'CUTYPE' => 'INDIVIDUAL',
139
+            'ROLE' => 'REQ-PARTICIPANT',
140
+            'PARTSTAT' => 'ACCEPTED',
141
+        ]);
142
+
143
+    }
144
+
145
+    /**
146
+     * @dataProvider searchProvider
147
+     */
148
+    public function testSearch($search1, $search2, $expected): void {
149
+        /** @var ICalendar | MockObject $calendar1 */
150
+        $calendar1 = $this->createMock(ICalendar::class);
151
+        $calendar1->method('getKey')->willReturn('simple:1');
152
+        $calendar1->expects($this->once())
153
+            ->method('search')
154
+            ->with('', [], [], null, null)
155
+            ->willReturn($search1);
156
+
157
+        /** @var ICalendar | MockObject $calendar2 */
158
+        $calendar2 = $this->createMock(ICalendar::class);
159
+        $calendar2->method('getKey')->willReturn('simple:2');
160
+        $calendar2->expects($this->once())
161
+            ->method('search')
162
+            ->with('', [], [], null, null)
163
+            ->willReturn($search2);
164
+
165
+        $this->manager->registerCalendar($calendar1);
166
+        $this->manager->registerCalendar($calendar2);
167
+
168
+        $result = $this->manager->search('');
169
+        $this->assertEquals($expected, $result);
170
+    }
171
+
172
+    /**
173
+     * @dataProvider searchProvider
174
+     */
175
+    public function testSearchOptions($search1, $search2, $expected): void {
176
+        /** @var ICalendar | MockObject $calendar1 */
177
+        $calendar1 = $this->createMock(ICalendar::class);
178
+        $calendar1->method('getKey')->willReturn('simple:1');
179
+        $calendar1->expects($this->once())
180
+            ->method('search')
181
+            ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
182
+                ['timerange' => ['start' => null, 'end' => null]], 5, 20)
183
+            ->willReturn($search1);
184
+
185
+        /** @var ICalendar | MockObject $calendar2 */
186
+        $calendar2 = $this->createMock(ICalendar::class);
187
+        $calendar2->method('getKey')->willReturn('simple:2');
188
+        $calendar2->expects($this->once())
189
+            ->method('search')
190
+            ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
191
+                ['timerange' => ['start' => null, 'end' => null]], 5, 20)
192
+            ->willReturn($search2);
193
+
194
+        $this->manager->registerCalendar($calendar1);
195
+        $this->manager->registerCalendar($calendar2);
196
+
197
+        $result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
198
+            ['timerange' => ['start' => null, 'end' => null]], 5, 20);
199
+        $this->assertEquals($expected, $result);
200
+    }
201
+
202
+    public function searchProvider() {
203
+        $search1 = [
204
+            [
205
+                'id' => 1,
206
+                'data' => 'foobar',
207
+            ],
208
+            [
209
+                'id' => 2,
210
+                'data' => 'barfoo',
211
+            ]
212
+        ];
213
+        $search2 = [
214
+            [
215
+                'id' => 3,
216
+                'data' => 'blablub',
217
+            ],
218
+            [
219
+                'id' => 4,
220
+                'data' => 'blubbla',
221
+            ]
222
+        ];
223
+
224
+        $expected = [
225
+            [
226
+                'id' => 1,
227
+                'data' => 'foobar',
228
+                'calendar-key' => 'simple:1',
229
+            ],
230
+            [
231
+                'id' => 2,
232
+                'data' => 'barfoo',
233
+                'calendar-key' => 'simple:1',
234
+            ],
235
+            [
236
+                'id' => 3,
237
+                'data' => 'blablub',
238
+                'calendar-key' => 'simple:2',
239
+            ],
240
+            [
241
+                'id' => 4,
242
+                'data' => 'blubbla',
243
+                'calendar-key' => 'simple:2',
244
+            ]
245
+        ];
246
+
247
+        return [
248
+            [
249
+                $search1,
250
+                $search2,
251
+                $expected
252
+            ]
253
+        ];
254
+    }
255
+
256
+    public function testRegisterUnregister(): void {
257
+        /** @var ICalendar | MockObject $calendar1 */
258
+        $calendar1 = $this->createMock(ICalendar::class);
259
+        $calendar1->method('getKey')->willReturn('key1');
260
+
261
+        /** @var ICalendar | MockObject $calendar2 */
262
+        $calendar2 = $this->createMock(ICalendar::class);
263
+        $calendar2->method('getKey')->willReturn('key2');
264
+
265
+        $this->manager->registerCalendar($calendar1);
266
+        $this->manager->registerCalendar($calendar2);
267
+
268
+        $result = $this->manager->getCalendars();
269
+        $this->assertCount(2, $result);
270
+        $this->assertContains($calendar1, $result);
271
+        $this->assertContains($calendar2, $result);
272
+
273
+        $this->manager->unregisterCalendar($calendar1);
274
+
275
+        $result = $this->manager->getCalendars();
276
+        $this->assertCount(1, $result);
277
+        $this->assertContains($calendar2, $result);
278
+    }
279
+
280
+    public function testGetCalendars(): void {
281
+        /** @var ICalendar | MockObject $calendar1 */
282
+        $calendar1 = $this->createMock(ICalendar::class);
283
+        $calendar1->method('getKey')->willReturn('key1');
284
+
285
+        /** @var ICalendar | MockObject $calendar2 */
286
+        $calendar2 = $this->createMock(ICalendar::class);
287
+        $calendar2->method('getKey')->willReturn('key2');
288
+
289
+        $this->manager->registerCalendar($calendar1);
290
+        $this->manager->registerCalendar($calendar2);
291
+
292
+        $result = $this->manager->getCalendars();
293
+        $this->assertCount(2, $result);
294
+        $this->assertContains($calendar1, $result);
295
+        $this->assertContains($calendar2, $result);
296
+
297
+        $this->manager->clear();
298
+
299
+        $result = $this->manager->getCalendars();
300
+
301
+        $this->assertCount(0, $result);
302
+    }
303
+
304
+    public function testEnabledIfNot(): void {
305
+        $isEnabled = $this->manager->isEnabled();
306
+        $this->assertFalse($isEnabled);
307
+    }
308
+
309
+    public function testIfEnabledIfSo(): void {
310
+        /** @var ICalendar | MockObject $calendar */
311
+        $calendar = $this->createMock(ICalendar::class);
312
+        $this->manager->registerCalendar($calendar);
313
+
314
+        $isEnabled = $this->manager->isEnabled();
315
+        $this->assertTrue($isEnabled);
316
+    }
317
+
318
+    public function testHandleImipRequestWithNoCalendars(): void {
319
+        // construct calendar manager returns
320
+        /** @var Manager&MockObject $manager */
321
+        $manager = $this->getMockBuilder(Manager::class)
322
+            ->setConstructorArgs([
323
+                $this->coordinator,
324
+                $this->container,
325
+                $this->logger,
326
+                $this->time,
327
+                $this->secureRandom,
328
+                $this->userManager,
329
+                $this->serverFactory,
330
+            ])
331
+            ->onlyMethods(['getCalendarsForPrincipal'])
332
+            ->getMock();
333
+        $manager->expects(self::once())
334
+            ->method('getCalendarsForPrincipal')
335
+            ->willReturn([]);
336
+        // construct logger returns
337
+        $this->logger->expects(self::once())->method('warning')
338
+            ->with('iMip message could not be processed because user has no calendars');
339
+        // construct parameters
340
+        $principalUri = 'principals/user/attendee1';
341
+        $sender = '[email protected]';
342
+        $recipient = '[email protected]';
343
+        $calendar = $this->vCalendar1a;
344
+        $calendar->add('METHOD', 'REQUEST');
345
+        // Act
346
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
347
+        // Assert
348
+        $this->assertFalse($result);
349
+    }
350
+
351
+    public function testHandleImipRequestWithInvalidData(): void {
352
+        // construct mock user calendar
353
+        $userCalendar = $this->createMock(ITestCalendar::class);
354
+        // construct mock calendar manager and returns
355
+        /** @var Manager&MockObject $manager */
356
+        $manager = $this->getMockBuilder(Manager::class)
357
+            ->setConstructorArgs([
358
+                $this->coordinator,
359
+                $this->container,
360
+                $this->logger,
361
+                $this->time,
362
+                $this->secureRandom,
363
+                $this->userManager,
364
+                $this->serverFactory,
365
+            ])
366
+            ->onlyMethods(['getCalendarsForPrincipal'])
367
+            ->getMock();
368
+        $manager->expects(self::once())
369
+            ->method('getCalendarsForPrincipal')
370
+            ->willReturn([$userCalendar]);
371
+        // construct logger returns
372
+        $this->logger->expects(self::once())->method('error')
373
+            ->with('iMip message could not be processed because an error occurred while parsing the iMip message');
374
+        // construct parameters
375
+        $principalUri = 'principals/user/attendee1';
376
+        $sender = '[email protected]';
377
+        $recipient = '[email protected]';
378
+        // Act
379
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, 'Invalid data');
380
+        // Assert
381
+        $this->assertFalse($result);
382
+    }
383
+
384
+    public function testHandleImipRequestWithNoMethod(): void {
385
+        // construct mock user calendar
386
+        $userCalendar = $this->createMock(ITestCalendar::class);
387
+        // construct mock calendar manager and returns
388
+        /** @var Manager&MockObject $manager */
389
+        $manager = $this->getMockBuilder(Manager::class)
390
+            ->setConstructorArgs([
391
+                $this->coordinator,
392
+                $this->container,
393
+                $this->logger,
394
+                $this->time,
395
+                $this->secureRandom,
396
+                $this->userManager,
397
+                $this->serverFactory,
398
+            ])
399
+            ->onlyMethods(['getCalendarsForPrincipal'])
400
+            ->getMock();
401
+        $manager->expects(self::once())
402
+            ->method('getCalendarsForPrincipal')
403
+            ->willReturn([$userCalendar]);
404
+        // construct logger returns
405
+        $this->logger->expects(self::once())->method('warning')
406
+            ->with('iMip message contains an incorrect or invalid method');
407
+        // construct parameters
408
+        $principalUri = 'principals/user/attendee1';
409
+        $sender = '[email protected]';
410
+        $recipient = '[email protected]';
411
+        $calendar = $this->vCalendar1a;
412
+        // Act
413
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
414
+        // Assert
415
+        $this->assertFalse($result);
416
+    }
417
+
418
+    public function testHandleImipRequestWithInvalidMethod(): void {
419
+        // construct mock user calendar
420
+        $userCalendar = $this->createMock(ITestCalendar::class);
421
+        // construct mock calendar manager and returns
422
+        /** @var Manager&MockObject $manager */
423
+        $manager = $this->getMockBuilder(Manager::class)
424
+            ->setConstructorArgs([
425
+                $this->coordinator,
426
+                $this->container,
427
+                $this->logger,
428
+                $this->time,
429
+                $this->secureRandom,
430
+                $this->userManager,
431
+                $this->serverFactory,
432
+            ])
433
+            ->onlyMethods(['getCalendarsForPrincipal'])
434
+            ->getMock();
435
+        $manager->expects(self::once())
436
+            ->method('getCalendarsForPrincipal')
437
+            ->willReturn([$userCalendar]);
438
+        // construct logger returns
439
+        $this->logger->expects(self::once())->method('warning')
440
+            ->with('iMip message contains an incorrect or invalid method');
441
+        // construct parameters
442
+        $principalUri = 'principals/user/attendee1';
443
+        $sender = '[email protected]';
444
+        $recipient = '[email protected]';
445
+        $calendar = $this->vCalendar1a;
446
+        $calendar->add('METHOD', 'CANCEL');
447
+        // Act
448
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
449
+        // Assert
450
+        $this->assertFalse($result);
451
+    }
452
+
453
+    public function testHandleImipRequestWithNoEvent(): void {
454
+        // construct mock user calendar
455
+        $userCalendar = $this->createMock(ITestCalendar::class);
456
+        // construct mock calendar manager and returns
457
+        /** @var Manager&MockObject $manager */
458
+        $manager = $this->getMockBuilder(Manager::class)
459
+            ->setConstructorArgs([
460
+                $this->coordinator,
461
+                $this->container,
462
+                $this->logger,
463
+                $this->time,
464
+                $this->secureRandom,
465
+                $this->userManager,
466
+                $this->serverFactory,
467
+            ])
468
+            ->onlyMethods(['getCalendarsForPrincipal'])
469
+            ->getMock();
470
+        $manager->expects(self::once())
471
+            ->method('getCalendarsForPrincipal')
472
+            ->willReturn([$userCalendar]);
473
+        // construct logger returns
474
+        $this->logger->expects(self::once())->method('warning')
475
+            ->with('iMip message contains no event');
476
+        // construct parameters
477
+        $principalUri = 'principals/user/attendee1';
478
+        $sender = '[email protected]';
479
+        $recipient = '[email protected]';
480
+        $calendar = $this->vCalendar1a;
481
+        $calendar->add('METHOD', 'REQUEST');
482
+        $calendar->remove('VEVENT');
483
+        // Act
484
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
485
+        // Assert
486
+        $this->assertFalse($result);
487
+    }
488
+
489
+    public function testHandleImipRequestWithNoUid(): void {
490
+        // construct mock user calendar
491
+        $userCalendar = $this->createMock(ITestCalendar::class);
492
+        // construct mock calendar manager and returns
493
+        /** @var Manager&MockObject $manager */
494
+        $manager = $this->getMockBuilder(Manager::class)
495
+            ->setConstructorArgs([
496
+                $this->coordinator,
497
+                $this->container,
498
+                $this->logger,
499
+                $this->time,
500
+                $this->secureRandom,
501
+                $this->userManager,
502
+                $this->serverFactory,
503
+            ])
504
+            ->onlyMethods(['getCalendarsForPrincipal'])
505
+            ->getMock();
506
+        $manager->expects(self::once())
507
+            ->method('getCalendarsForPrincipal')
508
+            ->willReturn([$userCalendar]);
509
+        // construct logger returns
510
+        $this->logger->expects(self::once())->method('warning')
511
+            ->with('iMip message event dose not contains a UID');
512
+        // construct parameters
513
+        $principalUri = 'principals/user/attendee1';
514
+        $sender = '[email protected]';
515
+        $recipient = '[email protected]';
516
+        $calendar = $this->vCalendar1a;
517
+        $calendar->add('METHOD', 'REQUEST');
518
+        $calendar->VEVENT->remove('UID');
519
+        // Act
520
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
521
+        // Assert
522
+        $this->assertFalse($result);
523
+    }
524
+
525
+    public function testHandleImipRequestWithNoOrganizer(): void {
526
+        // construct mock user calendar
527
+        $userCalendar = $this->createMock(ITestCalendar::class);
528
+        // construct mock calendar manager and returns
529
+        /** @var Manager&MockObject $manager */
530
+        $manager = $this->getMockBuilder(Manager::class)
531
+            ->setConstructorArgs([
532
+                $this->coordinator,
533
+                $this->container,
534
+                $this->logger,
535
+                $this->time,
536
+                $this->secureRandom,
537
+                $this->userManager,
538
+                $this->serverFactory,
539
+            ])
540
+            ->onlyMethods(['getCalendarsForPrincipal'])
541
+            ->getMock();
542
+        $manager->expects(self::once())
543
+            ->method('getCalendarsForPrincipal')
544
+            ->willReturn([$userCalendar]);
545
+        // construct logger returns
546
+        $this->logger->expects(self::once())->method('warning')
547
+            ->with('iMip message event dose not contains an organizer');
548
+        // construct parameters
549
+        $principalUri = 'principals/user/attendee1';
550
+        $sender = '[email protected]';
551
+        $recipient = '[email protected]';
552
+        $calendar = $this->vCalendar1a;
553
+        $calendar->add('METHOD', 'REQUEST');
554
+        $calendar->VEVENT->remove('ORGANIZER');
555
+        // Act
556
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
557
+        // Assert
558
+        $this->assertFalse($result);
559
+    }
560
+
561
+    public function testHandleImipRequestWithNoAttendee(): void {
562
+        // construct mock user calendar
563
+        $userCalendar = $this->createMock(ITestCalendar::class);
564
+        // construct mock calendar manager and returns
565
+        /** @var Manager&MockObject $manager */
566
+        $manager = $this->getMockBuilder(Manager::class)
567
+            ->setConstructorArgs([
568
+                $this->coordinator,
569
+                $this->container,
570
+                $this->logger,
571
+                $this->time,
572
+                $this->secureRandom,
573
+                $this->userManager,
574
+                $this->serverFactory,
575
+            ])
576
+            ->onlyMethods(['getCalendarsForPrincipal'])
577
+            ->getMock();
578
+        $manager->expects(self::once())
579
+            ->method('getCalendarsForPrincipal')
580
+            ->willReturn([$userCalendar]);
581
+        // construct logger returns
582
+        $this->logger->expects(self::once())->method('warning')
583
+            ->with('iMip message event dose not contains any attendees');
584
+        // construct parameters
585
+        $principalUri = 'principals/user/attendee1';
586
+        $sender = '[email protected]';
587
+        $recipient = '[email protected]';
588
+        $calendar = $this->vCalendar1a;
589
+        $calendar->add('METHOD', 'REQUEST');
590
+        $calendar->VEVENT->remove('ATTENDEE');
591
+        // Act
592
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
593
+        // Assert
594
+        $this->assertFalse($result);
595
+    }
596
+
597
+    public function testHandleImipRequestWithInvalidAttendee(): void {
598
+        // construct mock user calendar
599
+        $userCalendar = $this->createMock(ITestCalendar::class);
600
+        // construct mock calendar manager and returns
601
+        /** @var Manager&MockObject $manager */
602
+        $manager = $this->getMockBuilder(Manager::class)
603
+            ->setConstructorArgs([
604
+                $this->coordinator,
605
+                $this->container,
606
+                $this->logger,
607
+                $this->time,
608
+                $this->secureRandom,
609
+                $this->userManager,
610
+                $this->serverFactory,
611
+            ])
612
+            ->onlyMethods(['getCalendarsForPrincipal'])
613
+            ->getMock();
614
+        $manager->expects(self::once())
615
+            ->method('getCalendarsForPrincipal')
616
+            ->willReturn([$userCalendar]);
617
+        // construct logger returns
618
+        $this->logger->expects(self::once())->method('warning')
619
+            ->with('iMip message event does not contain a attendee that matches the recipient');
620
+        // construct parameters
621
+        $principalUri = 'principals/user/attendee1';
622
+        $sender = '[email protected]';
623
+        $recipient = '[email protected]';
624
+        $calendar = $this->vCalendar1a;
625
+        $calendar->add('METHOD', 'REQUEST');
626
+        // Act
627
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
628
+        // Assert
629
+        $this->assertFalse($result);
630
+    }
631
+
632
+    public function testHandleImipRequestWithNoMatch(): void {
633
+        // construct mock user calendar
634
+        $userCalendar = $this->createMock(ITestCalendar::class);
635
+        $userCalendar->expects(self::once())
636
+            ->method('isDeleted')
637
+            ->willReturn(false);
638
+        $userCalendar->expects(self::once())
639
+            ->method('isWritable')
640
+            ->willReturn(true);
641
+        $userCalendar->expects(self::once())
642
+            ->method('isShared')
643
+            ->willReturn(false);
644
+        $userCalendar->expects(self::once())
645
+            ->method('search')
646
+            ->willReturn([]);
647
+        // construct mock calendar manager and returns
648
+        /** @var Manager&MockObject $manager */
649
+        $manager = $this->getMockBuilder(Manager::class)
650
+            ->setConstructorArgs([
651
+                $this->coordinator,
652
+                $this->container,
653
+                $this->logger,
654
+                $this->time,
655
+                $this->secureRandom,
656
+                $this->userManager,
657
+                $this->serverFactory,
658
+            ])
659
+            ->onlyMethods(['getCalendarsForPrincipal'])
660
+            ->getMock();
661
+        $manager->expects(self::once())
662
+            ->method('getCalendarsForPrincipal')
663
+            ->willReturn([$userCalendar]);
664
+        // construct logger returns
665
+        $this->logger->expects(self::once())->method('warning')
666
+            ->with('iMip message event could not be processed because no corresponding event was found in any calendar');
667
+        // construct parameters
668
+        $principalUri = 'principals/user/attendee1';
669
+        $sender = '[email protected]';
670
+        $recipient = '[email protected]';
671
+        $calendar = $this->vCalendar1a;
672
+        $calendar->add('METHOD', 'REQUEST');
673
+        // Act
674
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
675
+        // Assert
676
+        $this->assertFalse($result);
677
+    }
678
+
679
+    public function testHandleImipRequest(): void {
680
+        // construct mock user calendar
681
+        $userCalendar = $this->createMock(ITestCalendar::class);
682
+        $userCalendar->expects(self::once())
683
+            ->method('isDeleted')
684
+            ->willReturn(false);
685
+        $userCalendar->expects(self::once())
686
+            ->method('isWritable')
687
+            ->willReturn(true);
688
+        $userCalendar->expects(self::once())
689
+            ->method('isShared')
690
+            ->willReturn(false);
691
+        $userCalendar->expects(self::once())
692
+            ->method('search')
693
+            ->willReturn([['uri' => 'principals/user/attendee1/personal']]);
694
+        // construct mock calendar manager and returns
695
+        /** @var Manager&MockObject $manager */
696
+        $manager = $this->getMockBuilder(Manager::class)
697
+            ->setConstructorArgs([
698
+                $this->coordinator,
699
+                $this->container,
700
+                $this->logger,
701
+                $this->time,
702
+                $this->secureRandom,
703
+                $this->userManager,
704
+                $this->serverFactory,
705
+            ])
706
+            ->onlyMethods(['getCalendarsForPrincipal'])
707
+            ->getMock();
708
+        $manager->expects(self::once())
709
+            ->method('getCalendarsForPrincipal')
710
+            ->willReturn([$userCalendar]);
711
+        // construct parameters
712
+        $principalUri = 'principals/user/attendee1';
713
+        $sender = '[email protected]';
714
+        $recipient = '[email protected]';
715
+        $calendar = $this->vCalendar1a;
716
+        $calendar->add('METHOD', 'REQUEST');
717
+        // construct user calendar returns
718
+        $userCalendar->expects(self::once())
719
+            ->method('handleIMipMessage')
720
+            ->with('', $calendar->serialize());
721
+        // Act
722
+        $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendar->serialize());
723
+        // Assert
724
+        $this->assertTrue($result);
725
+    }
726
+
727
+    public function testHandleImipReplyWithNoCalendars(): void {
728
+        // construct calendar manager returns
729
+        /** @var Manager&MockObject $manager */
730
+        $manager = $this->getMockBuilder(Manager::class)
731
+            ->setConstructorArgs([
732
+                $this->coordinator,
733
+                $this->container,
734
+                $this->logger,
735
+                $this->time,
736
+                $this->secureRandom,
737
+                $this->userManager,
738
+                $this->serverFactory,
739
+            ])
740
+            ->onlyMethods(['getCalendarsForPrincipal'])
741
+            ->getMock();
742
+        $manager->expects(self::once())
743
+            ->method('getCalendarsForPrincipal')
744
+            ->willReturn([]);
745
+        // construct logger returns
746
+        $this->logger->expects(self::once())->method('warning')
747
+            ->with('iMip message could not be processed because user has no calendars');
748
+        // construct parameters
749
+        $principalUri = 'principals/user/linus';
750
+        $sender = '[email protected]';
751
+        $recipient = '[email protected]';
752
+        $calendar = $this->vCalendar2a;
753
+        $calendar->add('METHOD', 'REPLY');
754
+        // Act
755
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
756
+        // Assert
757
+        $this->assertFalse($result);
758
+    }
759
+
760
+    public function testHandleImipReplyWithInvalidData(): void {
761
+        // construct mock user calendar
762
+        $userCalendar = $this->createMock(ITestCalendar::class);
763
+        // construct mock calendar manager and returns
764
+        /** @var Manager&MockObject $manager */
765
+        $manager = $this->getMockBuilder(Manager::class)
766
+            ->setConstructorArgs([
767
+                $this->coordinator,
768
+                $this->container,
769
+                $this->logger,
770
+                $this->time,
771
+                $this->secureRandom,
772
+                $this->userManager,
773
+                $this->serverFactory,
774
+            ])
775
+            ->onlyMethods(['getCalendarsForPrincipal'])
776
+            ->getMock();
777
+        $manager->expects(self::once())
778
+            ->method('getCalendarsForPrincipal')
779
+            ->willReturn([$userCalendar]);
780
+        // construct logger returns
781
+        $this->logger->expects(self::once())->method('error')
782
+            ->with('iMip message could not be processed because an error occurred while parsing the iMip message');
783
+        // construct parameters
784
+        $principalUri = 'principals/user/attendee1';
785
+        $sender = '[email protected]';
786
+        $recipient = '[email protected]';
787
+        // Act
788
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, 'Invalid data');
789
+        // Assert
790
+        $this->assertFalse($result);
791
+    }
792
+
793
+    public function testHandleImipReplyWithNoMethod(): void {
794
+        // construct mock user calendar
795
+        $userCalendar = $this->createMock(ITestCalendar::class);
796
+        // construct mock calendar manager and returns
797
+        /** @var Manager&MockObject $manager */
798
+        $manager = $this->getMockBuilder(Manager::class)
799
+            ->setConstructorArgs([
800
+                $this->coordinator,
801
+                $this->container,
802
+                $this->logger,
803
+                $this->time,
804
+                $this->secureRandom,
805
+                $this->userManager,
806
+                $this->serverFactory,
807
+            ])
808
+            ->onlyMethods(['getCalendarsForPrincipal'])
809
+            ->getMock();
810
+        $manager->expects(self::once())
811
+            ->method('getCalendarsForPrincipal')
812
+            ->willReturn([$userCalendar]);
813
+        // construct logger returns
814
+        $this->logger->expects(self::once())->method('warning')
815
+            ->with('iMip message contains an incorrect or invalid method');
816
+        // construct parameters
817
+        $principalUri = 'principals/user/linus';
818
+        $sender = '[email protected]';
819
+        $recipient = '[email protected]';
820
+        $calendar = $this->vCalendar2a;
821
+        // Act
822
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
823
+        // Assert
824
+        $this->assertFalse($result);
825
+    }
826
+
827
+    public function testHandleImipReplyWithInvalidMethod(): void {
828
+        // construct mock user calendar
829
+        $userCalendar = $this->createMock(ITestCalendar::class);
830
+        // construct mock calendar manager and returns
831
+        /** @var Manager&MockObject $manager */
832
+        $manager = $this->getMockBuilder(Manager::class)
833
+            ->setConstructorArgs([
834
+                $this->coordinator,
835
+                $this->container,
836
+                $this->logger,
837
+                $this->time,
838
+                $this->secureRandom,
839
+                $this->userManager,
840
+                $this->serverFactory,
841
+            ])
842
+            ->onlyMethods(['getCalendarsForPrincipal'])
843
+            ->getMock();
844
+        $manager->expects(self::once())
845
+            ->method('getCalendarsForPrincipal')
846
+            ->willReturn([$userCalendar]);
847
+        // construct logger returns
848
+        $this->logger->expects(self::once())->method('warning')
849
+            ->with('iMip message contains an incorrect or invalid method');
850
+        // construct parameters
851
+        $principalUri = 'principals/user/linus';
852
+        $sender = '[email protected]';
853
+        $recipient = '[email protected]';
854
+        $calendar = $this->vCalendar2a;
855
+        $calendar->add('METHOD', 'UNKNOWN');
856
+        // Act
857
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
858
+        // Assert
859
+        $this->assertFalse($result);
860
+    }
861
+
862
+    public function testHandleImipReplyWithNoEvent(): void {
863
+        // construct mock user calendar
864
+        $userCalendar = $this->createMock(ITestCalendar::class);
865
+        // construct mock calendar manager and returns
866
+        /** @var Manager&MockObject $manager */
867
+        $manager = $this->getMockBuilder(Manager::class)
868
+            ->setConstructorArgs([
869
+                $this->coordinator,
870
+                $this->container,
871
+                $this->logger,
872
+                $this->time,
873
+                $this->secureRandom,
874
+                $this->userManager,
875
+                $this->serverFactory,
876
+            ])
877
+            ->onlyMethods(['getCalendarsForPrincipal'])
878
+            ->getMock();
879
+        $manager->expects(self::once())
880
+            ->method('getCalendarsForPrincipal')
881
+            ->willReturn([$userCalendar]);
882
+        // construct logger returns
883
+        $this->logger->expects(self::once())->method('warning')
884
+            ->with('iMip message contains no event');
885
+        // construct parameters
886
+        $principalUri = 'principals/user/linus';
887
+        $sender = '[email protected]';
888
+        $recipient = '[email protected]';
889
+        $calendar = $this->vCalendar2a;
890
+        $calendar->add('METHOD', 'REPLY');
891
+        $calendar->remove('VEVENT');
892
+        // Act
893
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
894
+        // Assert
895
+        $this->assertFalse($result);
896
+    }
897
+
898
+    public function testHandleImipReplyWithNoUid(): void {
899
+        // construct mock user calendar
900
+        $userCalendar = $this->createMock(ITestCalendar::class);
901
+        // construct mock calendar manager and returns
902
+        /** @var Manager&MockObject $manager */
903
+        $manager = $this->getMockBuilder(Manager::class)
904
+            ->setConstructorArgs([
905
+                $this->coordinator,
906
+                $this->container,
907
+                $this->logger,
908
+                $this->time,
909
+                $this->secureRandom,
910
+                $this->userManager,
911
+                $this->serverFactory,
912
+            ])
913
+            ->onlyMethods(['getCalendarsForPrincipal'])
914
+            ->getMock();
915
+        $manager->expects(self::once())
916
+            ->method('getCalendarsForPrincipal')
917
+            ->willReturn([$userCalendar]);
918
+        // construct logger returns
919
+        $this->logger->expects(self::once())->method('warning')
920
+            ->with('iMip message event dose not contains a UID');
921
+        // construct parameters
922
+        $principalUri = 'principals/user/linus';
923
+        $sender = '[email protected]';
924
+        $recipient = '[email protected]';
925
+        $calendar = $this->vCalendar2a;
926
+        $calendar->add('METHOD', 'REPLY');
927
+        $calendar->VEVENT->remove('UID');
928
+        // Act
929
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
930
+        // Assert
931
+        $this->assertFalse($result);
932
+    }
933
+
934
+    public function testHandleImipReplyWithNoOrganizer(): void {
935
+        // construct mock user calendar
936
+        $userCalendar = $this->createMock(ITestCalendar::class);
937
+        // construct mock calendar manager and returns
938
+        /** @var Manager&MockObject $manager */
939
+        $manager = $this->getMockBuilder(Manager::class)
940
+            ->setConstructorArgs([
941
+                $this->coordinator,
942
+                $this->container,
943
+                $this->logger,
944
+                $this->time,
945
+                $this->secureRandom,
946
+                $this->userManager,
947
+                $this->serverFactory,
948
+            ])
949
+            ->onlyMethods(['getCalendarsForPrincipal'])
950
+            ->getMock();
951
+        $manager->expects(self::once())
952
+            ->method('getCalendarsForPrincipal')
953
+            ->willReturn([$userCalendar]);
954
+        // construct logger returns
955
+        $this->logger->expects(self::once())->method('warning')
956
+            ->with('iMip message event dose not contains an organizer');
957
+        // construct parameters
958
+        $principalUri = 'principals/user/linus';
959
+        $sender = '[email protected]';
960
+        $recipient = '[email protected]';
961
+        $calendar = $this->vCalendar2a;
962
+        $calendar->add('METHOD', 'REPLY');
963
+        $calendar->VEVENT->remove('ORGANIZER');
964
+        // Act
965
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
966
+        // Assert
967
+        $this->assertFalse($result);
968
+    }
969
+
970
+    public function testHandleImipReplyWithNoAttendee(): void {
971
+        // construct mock user calendar
972
+        $userCalendar = $this->createMock(ITestCalendar::class);
973
+        // construct mock calendar manager and returns
974
+        /** @var Manager&MockObject $manager */
975
+        $manager = $this->getMockBuilder(Manager::class)
976
+            ->setConstructorArgs([
977
+                $this->coordinator,
978
+                $this->container,
979
+                $this->logger,
980
+                $this->time,
981
+                $this->secureRandom,
982
+                $this->userManager,
983
+                $this->serverFactory,
984
+            ])
985
+            ->onlyMethods(['getCalendarsForPrincipal'])
986
+            ->getMock();
987
+        $manager->expects(self::once())
988
+            ->method('getCalendarsForPrincipal')
989
+            ->willReturn([$userCalendar]);
990
+        // construct logger returns
991
+        $this->logger->expects(self::once())->method('warning')
992
+            ->with('iMip message event dose not contains any attendees');
993
+        // construct parameters
994
+        $principalUri = 'principals/user/linus';
995
+        $sender = '[email protected]';
996
+        $recipient = '[email protected]';
997
+        $calendar = $this->vCalendar2a;
998
+        $calendar->add('METHOD', 'REPLY');
999
+        $calendar->VEVENT->remove('ATTENDEE');
1000
+        // Act
1001
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendar->serialize());
1002
+        // Assert
1003
+        $this->assertFalse($result);
1004
+    }
1005
+
1006
+    public function testHandleImipReplyDateInThePast(): void {
1007
+        // construct mock user calendar
1008
+        $userCalendar = $this->createMock(ITestCalendar::class);
1009
+        // construct mock calendar manager and returns
1010
+        /** @var Manager&MockObject $manager */
1011
+        $manager = $this->getMockBuilder(Manager::class)
1012
+            ->setConstructorArgs([
1013
+                $this->coordinator,
1014
+                $this->container,
1015
+                $this->logger,
1016
+                $this->time,
1017
+                $this->secureRandom,
1018
+                $this->userManager,
1019
+                $this->serverFactory,
1020
+            ])
1021
+            ->onlyMethods(['getCalendarsForPrincipal'])
1022
+            ->getMock();
1023
+        $manager->expects(self::once())
1024
+            ->method('getCalendarsForPrincipal')
1025
+            ->willReturn([$userCalendar]);
1026
+        // construct logger and time returns
1027
+        $this->logger->expects(self::once())->method('warning')
1028
+            ->with('iMip message event could not be processed because the event is in the past');
1029
+        $this->time->expects(self::once())
1030
+            ->method('getTime')
1031
+            ->willReturn(time());
1032
+        // construct parameters
1033
+        $principalUri = 'principals/user/linus';
1034
+        $sender = '[email protected]';
1035
+        $recipient = '[email protected]';
1036
+        $calendarData = clone $this->vCalendar2a;
1037
+        $calendarData->add('METHOD', 'REPLY');
1038
+        $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
1039
+        // Act
1040
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1041
+        // Assert
1042
+        $this->assertFalse($result);
1043
+    }
1044
+
1045
+    public function testHandleImipReplyEventNotFound(): void {
1046
+        // construct mock user calendar
1047
+        $userCalendar = $this->createMock(ITestCalendar::class);
1048
+        $userCalendar->expects(self::once())
1049
+            ->method('search')
1050
+            ->willReturn([]);
1051
+        // construct mock calendar manager and returns
1052
+        /** @var Manager&MockObject $manager */
1053
+        $manager = $this->getMockBuilder(Manager::class)
1054
+            ->setConstructorArgs([
1055
+                $this->coordinator,
1056
+                $this->container,
1057
+                $this->logger,
1058
+                $this->time,
1059
+                $this->secureRandom,
1060
+                $this->userManager,
1061
+                $this->serverFactory,
1062
+            ])
1063
+            ->onlyMethods(['getCalendarsForPrincipal'])
1064
+            ->getMock();
1065
+        $manager->expects(self::once())
1066
+            ->method('getCalendarsForPrincipal')
1067
+            ->willReturn([$userCalendar]);
1068
+        // construct time returns
1069
+        $this->time->expects(self::once())
1070
+            ->method('getTime')
1071
+            ->willReturn(1628374233);
1072
+        // construct parameters
1073
+        $principalUri = 'principals/user/linus';
1074
+        $sender = '[email protected]';
1075
+        $recipient = '[email protected]';
1076
+        $calendarData = clone $this->vCalendar2a;
1077
+        $calendarData->add('METHOD', 'REPLY');
1078
+        // construct logger return
1079
+        $this->logger->expects(self::once())->method('warning')
1080
+            ->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1081
+        // Act
1082
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1083
+        // Assert
1084
+        $this->assertFalse($result);
1085
+    }
1086
+
1087
+    public function testHandleImipReply(): void {
1088
+        /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1089
+        $manager = $this->getMockBuilder(Manager::class)
1090
+            ->setConstructorArgs([
1091
+                $this->coordinator,
1092
+                $this->container,
1093
+                $this->logger,
1094
+                $this->time,
1095
+                $this->secureRandom,
1096
+                $this->userManager,
1097
+                $this->serverFactory,
1098
+            ])
1099
+            ->setMethods([
1100
+                'getCalendarsForPrincipal'
1101
+            ])
1102
+            ->getMock();
1103
+        $calendar = $this->createMock(ITestCalendar::class);
1104
+        $principalUri = 'principals/user/linus';
1105
+        $sender = '[email protected]';
1106
+        $recipient = '[email protected]';
1107
+        $calendarData = clone $this->vCalendar2a;
1108
+        $calendarData->add('METHOD', 'REPLY');
1109
+
1110
+        $this->time->expects(self::once())
1111
+            ->method('getTime')
1112
+            ->willReturn(1628374233);
1113
+        $manager->expects(self::once())
1114
+            ->method('getCalendarsForPrincipal')
1115
+            ->willReturn([$calendar]);
1116
+        $calendar->expects(self::once())
1117
+            ->method('search')
1118
+            ->willReturn([['uri' => 'testname.ics']]);
1119
+        $calendar->expects(self::once())
1120
+            ->method('handleIMipMessage')
1121
+            ->with('testname.ics', $calendarData->serialize());
1122
+        // Act
1123
+        $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1124
+        // Assert
1125
+        $this->assertTrue($result);
1126
+    }
1127
+
1128
+    public function testHandleImipCancelWithNoCalendars(): void {
1129
+        // construct calendar manager returns
1130
+        /** @var Manager&MockObject $manager */
1131
+        $manager = $this->getMockBuilder(Manager::class)
1132
+            ->setConstructorArgs([
1133
+                $this->coordinator,
1134
+                $this->container,
1135
+                $this->logger,
1136
+                $this->time,
1137
+                $this->secureRandom,
1138
+                $this->userManager,
1139
+                $this->serverFactory,
1140
+            ])
1141
+            ->onlyMethods(['getCalendarsForPrincipal'])
1142
+            ->getMock();
1143
+        $manager->expects(self::once())
1144
+            ->method('getCalendarsForPrincipal')
1145
+            ->willReturn([]);
1146
+        // construct logger returns
1147
+        $this->logger->expects(self::once())->method('warning')
1148
+            ->with('iMip message could not be processed because user has no calendars');
1149
+        // construct parameters
1150
+        $principalUri = 'principals/user/pierre';
1151
+        $sender = '[email protected]';
1152
+        $recipient = '[email protected]';
1153
+        $replyTo = null;
1154
+        $calendar = $this->vCalendar3a;
1155
+        $calendar->add('METHOD', 'CANCEL');
1156
+        // Act
1157
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1158
+        // Assert
1159
+        $this->assertFalse($result);
1160
+    }
1161
+
1162
+    public function testHandleImipCancelWithInvalidData(): void {
1163
+        // construct mock user calendar
1164
+        $userCalendar = $this->createMock(ITestCalendar::class);
1165
+        // construct mock calendar manager and returns
1166
+        /** @var Manager&MockObject $manager */
1167
+        $manager = $this->getMockBuilder(Manager::class)
1168
+            ->setConstructorArgs([
1169
+                $this->coordinator,
1170
+                $this->container,
1171
+                $this->logger,
1172
+                $this->time,
1173
+                $this->secureRandom,
1174
+                $this->userManager,
1175
+                $this->serverFactory,
1176
+            ])
1177
+            ->onlyMethods(['getCalendarsForPrincipal'])
1178
+            ->getMock();
1179
+        $manager->expects(self::once())
1180
+            ->method('getCalendarsForPrincipal')
1181
+            ->willReturn([$userCalendar]);
1182
+        // construct logger returns
1183
+        $this->logger->expects(self::once())->method('error')
1184
+            ->with('iMip message could not be processed because an error occurred while parsing the iMip message');
1185
+        // construct parameters
1186
+        $principalUri = 'principals/user/attendee1';
1187
+        $sender = '[email protected]';
1188
+        $recipient = '[email protected]';
1189
+        $replyTo = null;
1190
+        // Act
1191
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, 'Invalid data');
1192
+        // Assert
1193
+        $this->assertFalse($result);
1194
+    }
1195
+
1196
+
1197
+    public function testHandleImipCancelWithNoMethod(): void {
1198
+        // construct mock user calendar
1199
+        $userCalendar = $this->createMock(ITestCalendar::class);
1200
+        // construct mock calendar manager and returns
1201
+        /** @var Manager&MockObject $manager */
1202
+        $manager = $this->getMockBuilder(Manager::class)
1203
+            ->setConstructorArgs([
1204
+                $this->coordinator,
1205
+                $this->container,
1206
+                $this->logger,
1207
+                $this->time,
1208
+                $this->secureRandom,
1209
+                $this->userManager,
1210
+                $this->serverFactory,
1211
+            ])
1212
+            ->onlyMethods(['getCalendarsForPrincipal'])
1213
+            ->getMock();
1214
+        $manager->expects(self::once())
1215
+            ->method('getCalendarsForPrincipal')
1216
+            ->willReturn([$userCalendar]);
1217
+        // construct logger returns
1218
+        $this->logger->expects(self::once())->method('warning')
1219
+            ->with('iMip message contains an incorrect or invalid method');
1220
+        // construct parameters
1221
+        $principalUri = 'principals/user/pierre';
1222
+        $sender = '[email protected]';
1223
+        $recipient = '[email protected]';
1224
+        $replyTo = null;
1225
+        $calendar = $this->vCalendar3a;
1226
+        // Act
1227
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1228
+        // Assert
1229
+        $this->assertFalse($result);
1230
+    }
1231
+
1232
+    public function testHandleImipCancelWithInvalidMethod(): void {
1233
+        // construct mock user calendar
1234
+        $userCalendar = $this->createMock(ITestCalendar::class);
1235
+        // construct mock calendar manager and returns
1236
+        /** @var Manager&MockObject $manager */
1237
+        $manager = $this->getMockBuilder(Manager::class)
1238
+            ->setConstructorArgs([
1239
+                $this->coordinator,
1240
+                $this->container,
1241
+                $this->logger,
1242
+                $this->time,
1243
+                $this->secureRandom,
1244
+                $this->userManager,
1245
+                $this->serverFactory,
1246
+            ])
1247
+            ->onlyMethods(['getCalendarsForPrincipal'])
1248
+            ->getMock();
1249
+        $manager->expects(self::once())
1250
+            ->method('getCalendarsForPrincipal')
1251
+            ->willReturn([$userCalendar]);
1252
+        // construct logger returns
1253
+        $this->logger->expects(self::once())->method('warning')
1254
+            ->with('iMip message contains an incorrect or invalid method');
1255
+        // construct parameters
1256
+        $principalUri = 'principals/user/pierre';
1257
+        $sender = '[email protected]';
1258
+        $recipient = '[email protected]';
1259
+        $replyTo = null;
1260
+        $calendar = $this->vCalendar3a;
1261
+        $calendar->add('METHOD', 'UNKNOWN');
1262
+        // Act
1263
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1264
+        // Assert
1265
+        $this->assertFalse($result);
1266
+    }
1267
+
1268
+    public function testHandleImipCancelWithNoEvent(): void {
1269
+        // construct mock user calendar
1270
+        $userCalendar = $this->createMock(ITestCalendar::class);
1271
+        // construct mock calendar manager and returns
1272
+        /** @var Manager&MockObject $manager */
1273
+        $manager = $this->getMockBuilder(Manager::class)
1274
+            ->setConstructorArgs([
1275
+                $this->coordinator,
1276
+                $this->container,
1277
+                $this->logger,
1278
+                $this->time,
1279
+                $this->secureRandom,
1280
+                $this->userManager,
1281
+                $this->serverFactory,
1282
+            ])
1283
+            ->onlyMethods(['getCalendarsForPrincipal'])
1284
+            ->getMock();
1285
+        $manager->expects(self::once())
1286
+            ->method('getCalendarsForPrincipal')
1287
+            ->willReturn([$userCalendar]);
1288
+        // construct logger returns
1289
+        $this->logger->expects(self::once())->method('warning')
1290
+            ->with('iMip message contains no event');
1291
+        // construct parameters
1292
+        $principalUri = 'principals/user/pierre';
1293
+        $sender = '[email protected]';
1294
+        $recipient = '[email protected]';
1295
+        $replyTo = null;
1296
+        $calendar = $this->vCalendar3a;
1297
+        $calendar->add('METHOD', 'CANCEL');
1298
+        $calendar->remove('VEVENT');
1299
+        // Act
1300
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1301
+        // Assert
1302
+        $this->assertFalse($result);
1303
+    }
1304
+
1305
+    public function testHandleImipCancelWithNoUid(): void {
1306
+        // construct mock user calendar
1307
+        $userCalendar = $this->createMock(ITestCalendar::class);
1308
+        // construct mock calendar manager and returns
1309
+        /** @var Manager&MockObject $manager */
1310
+        $manager = $this->getMockBuilder(Manager::class)
1311
+            ->setConstructorArgs([
1312
+                $this->coordinator,
1313
+                $this->container,
1314
+                $this->logger,
1315
+                $this->time,
1316
+                $this->secureRandom,
1317
+                $this->userManager,
1318
+                $this->serverFactory,
1319
+            ])
1320
+            ->onlyMethods(['getCalendarsForPrincipal'])
1321
+            ->getMock();
1322
+        $manager->expects(self::once())
1323
+            ->method('getCalendarsForPrincipal')
1324
+            ->willReturn([$userCalendar]);
1325
+        // construct logger returns
1326
+        $this->logger->expects(self::once())->method('warning')
1327
+            ->with('iMip message event dose not contains a UID');
1328
+        // construct parameters
1329
+        $principalUri = 'principals/user/pierre';
1330
+        $sender = '[email protected]';
1331
+        $recipient = '[email protected]';
1332
+        $replyTo = null;
1333
+        $calendar = $this->vCalendar3a;
1334
+        $calendar->add('METHOD', 'CANCEL');
1335
+        $calendar->VEVENT->remove('UID');
1336
+        // Act
1337
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1338
+        // Assert
1339
+        $this->assertFalse($result);
1340
+    }
1341
+
1342
+    public function testHandleImipCancelWithNoOrganizer(): void {
1343
+        // construct mock user calendar
1344
+        $userCalendar = $this->createMock(ITestCalendar::class);
1345
+        // construct mock calendar manager and returns
1346
+        /** @var Manager&MockObject $manager */
1347
+        $manager = $this->getMockBuilder(Manager::class)
1348
+            ->setConstructorArgs([
1349
+                $this->coordinator,
1350
+                $this->container,
1351
+                $this->logger,
1352
+                $this->time,
1353
+                $this->secureRandom,
1354
+                $this->userManager,
1355
+                $this->serverFactory,
1356
+            ])
1357
+            ->onlyMethods(['getCalendarsForPrincipal'])
1358
+            ->getMock();
1359
+        $manager->expects(self::once())
1360
+            ->method('getCalendarsForPrincipal')
1361
+            ->willReturn([$userCalendar]);
1362
+        // construct logger returns
1363
+        $this->logger->expects(self::once())->method('warning')
1364
+            ->with('iMip message event dose not contains an organizer');
1365
+        // construct parameters
1366
+        $principalUri = 'principals/user/pierre';
1367
+        $sender = '[email protected]';
1368
+        $recipient = '[email protected]';
1369
+        $replyTo = null;
1370
+        $calendar = $this->vCalendar3a;
1371
+        $calendar->add('METHOD', 'CANCEL');
1372
+        $calendar->VEVENT->remove('ORGANIZER');
1373
+        // Act
1374
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1375
+        // Assert
1376
+        $this->assertFalse($result);
1377
+    }
1378
+
1379
+    public function testHandleImipCancelWithNoAttendee(): void {
1380
+        // construct mock user calendar
1381
+        $userCalendar = $this->createMock(ITestCalendar::class);
1382
+        // construct mock calendar manager and returns
1383
+        /** @var Manager&MockObject $manager */
1384
+        $manager = $this->getMockBuilder(Manager::class)
1385
+            ->setConstructorArgs([
1386
+                $this->coordinator,
1387
+                $this->container,
1388
+                $this->logger,
1389
+                $this->time,
1390
+                $this->secureRandom,
1391
+                $this->userManager,
1392
+                $this->serverFactory,
1393
+            ])
1394
+            ->onlyMethods(['getCalendarsForPrincipal'])
1395
+            ->getMock();
1396
+        $manager->expects(self::once())
1397
+            ->method('getCalendarsForPrincipal')
1398
+            ->willReturn([$userCalendar]);
1399
+        // construct logger returns
1400
+        $this->logger->expects(self::once())->method('warning')
1401
+            ->with('iMip message event dose not contains any attendees');
1402
+        // construct parameters
1403
+        $principalUri = 'principals/user/pierre';
1404
+        $sender = '[email protected]';
1405
+        $recipient = '[email protected]';
1406
+        $replyTo = null;
1407
+        $calendar = $this->vCalendar3a;
1408
+        $calendar->add('METHOD', 'CANCEL');
1409
+        $calendar->VEVENT->remove('ATTENDEE');
1410
+        // Act
1411
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendar->serialize());
1412
+        // Assert
1413
+        $this->assertFalse($result);
1414
+    }
1415
+
1416
+    public function testHandleImipCancelAttendeeNotRecipient(): void {
1417
+        // construct mock user calendar
1418
+        $userCalendar = $this->createMock(ITestCalendar::class);
1419
+        // construct mock calendar manager and returns
1420
+        /** @var Manager&MockObject $manager */
1421
+        $manager = $this->getMockBuilder(Manager::class)
1422
+            ->setConstructorArgs([
1423
+                $this->coordinator,
1424
+                $this->container,
1425
+                $this->logger,
1426
+                $this->time,
1427
+                $this->secureRandom,
1428
+                $this->userManager,
1429
+                $this->serverFactory,
1430
+            ])
1431
+            ->onlyMethods(['getCalendarsForPrincipal'])
1432
+            ->getMock();
1433
+        $manager->expects(self::once())
1434
+            ->method('getCalendarsForPrincipal')
1435
+            ->willReturn([$userCalendar]);
1436
+        // construct logger returns
1437
+        $this->logger->expects(self::once())->method('warning')
1438
+            ->with('iMip message event could not be processed because recipient must be an ATTENDEE of this event');
1439
+        // construct parameters
1440
+        $principalUri = 'principals/user/pierre';
1441
+        $sender = '[email protected]';
1442
+        $recipient = '[email protected]';
1443
+        $replyTo = null;
1444
+        $calendarData = clone $this->vCalendar3a;
1445
+        $calendarData->add('METHOD', 'CANCEL');
1446
+        // Act
1447
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1448
+        // Assert
1449
+        $this->assertFalse($result);
1450
+    }
1451
+
1452
+    public function testHandleImipCancelDateInThePast(): void {
1453
+        // construct mock user calendar
1454
+        $userCalendar = $this->createMock(ITestCalendar::class);
1455
+        // construct mock calendar manager and returns
1456
+        /** @var Manager&MockObject $manager */
1457
+        $manager = $this->getMockBuilder(Manager::class)
1458
+            ->setConstructorArgs([
1459
+                $this->coordinator,
1460
+                $this->container,
1461
+                $this->logger,
1462
+                $this->time,
1463
+                $this->secureRandom,
1464
+                $this->userManager,
1465
+                $this->serverFactory,
1466
+            ])
1467
+            ->onlyMethods(['getCalendarsForPrincipal'])
1468
+            ->getMock();
1469
+        $manager->expects(self::once())
1470
+            ->method('getCalendarsForPrincipal')
1471
+            ->willReturn([$userCalendar]);
1472
+        // construct logger and time returns
1473
+        $this->logger->expects(self::once())->method('warning')
1474
+            ->with('iMip message event could not be processed because the event is in the past');
1475
+        $this->time->expects(self::once())
1476
+            ->method('getTime')
1477
+            ->willReturn(time());
1478
+        // construct parameters
1479
+        $principalUri = 'principals/user/pierre';
1480
+        $sender = '[email protected]';
1481
+        $recipient = '[email protected]';
1482
+        $replyTo = null;
1483
+        $calendarData = clone $this->vCalendar3a;
1484
+        $calendarData->add('METHOD', 'CANCEL');
1485
+        $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
1486
+        // Act
1487
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1488
+        // Assert
1489
+        $this->assertFalse($result);
1490
+    }
1491
+
1492
+    public function testHandleImipCancelEventNotFound(): void {
1493
+        // construct mock user calendar
1494
+        $userCalendar = $this->createMock(ITestCalendar::class);
1495
+        $userCalendar->expects(self::once())
1496
+            ->method('search')
1497
+            ->willReturn([]);
1498
+        // construct mock calendar manager and returns
1499
+        /** @var Manager&MockObject $manager */
1500
+        $manager = $this->getMockBuilder(Manager::class)
1501
+            ->setConstructorArgs([
1502
+                $this->coordinator,
1503
+                $this->container,
1504
+                $this->logger,
1505
+                $this->time,
1506
+                $this->secureRandom,
1507
+                $this->userManager,
1508
+                $this->serverFactory,
1509
+            ])
1510
+            ->onlyMethods(['getCalendarsForPrincipal'])
1511
+            ->getMock();
1512
+        $manager->expects(self::once())
1513
+            ->method('getCalendarsForPrincipal')
1514
+            ->willReturn([$userCalendar]);
1515
+        // construct time returns
1516
+        $this->time->expects(self::once())
1517
+            ->method('getTime')
1518
+            ->willReturn(1628374233);
1519
+        // construct parameters
1520
+        $principalUri = 'principals/user/pierre';
1521
+        $sender = '[email protected]';
1522
+        $recipient = '[email protected]';
1523
+        $replyTo = null;
1524
+        $calendarData = clone $this->vCalendar3a;
1525
+        $calendarData->add('METHOD', 'CANCEL');
1526
+        // construct logger return
1527
+        $this->logger->expects(self::once())->method('warning')
1528
+            ->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1529
+        // Act
1530
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1531
+        // Assert
1532
+        $this->assertFalse($result);
1533
+    }
1534
+
1535
+    public function testHandleImipCancelOrganiserInReplyTo(): void {
1536
+        /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1537
+        $manager = $this->getMockBuilder(Manager::class)
1538
+            ->setConstructorArgs([
1539
+                $this->coordinator,
1540
+                $this->container,
1541
+                $this->logger,
1542
+                $this->time,
1543
+                $this->secureRandom,
1544
+                $this->userManager,
1545
+                $this->serverFactory,
1546
+            ])
1547
+            ->setMethods([
1548
+                'getCalendarsForPrincipal'
1549
+            ])
1550
+            ->getMock();
1551 1551
 		
1552
-		$principalUri = 'principals/user/pierre';
1553
-		$sender = '[email protected]';
1554
-		$recipient = '[email protected]';
1555
-		$replyTo = '[email protected]';
1556
-		$calendar = $this->createMock(ITestCalendar::class);
1557
-		$calendarData = clone $this->vCalendar3a;
1558
-		$calendarData->add('METHOD', 'CANCEL');
1559
-
1560
-		$this->time->expects(self::once())
1561
-			->method('getTime')
1562
-			->willReturn(1628374233);
1563
-		$manager->expects(self::once())
1564
-			->method('getCalendarsForPrincipal')
1565
-			->with($principalUri)
1566
-			->willReturn([$calendar]);
1567
-		$calendar->expects(self::once())
1568
-			->method('search')
1569
-			->willReturn([['uri' => 'testname.ics']]);
1570
-		$calendar->expects(self::once())
1571
-			->method('handleIMipMessage')
1572
-			->with('testname.ics', $calendarData->serialize());
1573
-		// Act
1574
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1575
-		// Assert
1576
-		$this->assertTrue($result);
1577
-	}
1578
-
1579
-	public function testHandleImipCancel(): void {
1580
-		/** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1581
-		$manager = $this->getMockBuilder(Manager::class)
1582
-			->setConstructorArgs([
1583
-				$this->coordinator,
1584
-				$this->container,
1585
-				$this->logger,
1586
-				$this->time,
1587
-				$this->secureRandom,
1588
-				$this->userManager,
1589
-				$this->serverFactory,
1590
-			])
1591
-			->setMethods([
1592
-				'getCalendarsForPrincipal'
1593
-			])
1594
-			->getMock();
1595
-		$principalUri = 'principals/user/pierre';
1596
-		$sender = '[email protected]';
1597
-		$recipient = '[email protected]';
1598
-		$replyTo = null;
1599
-		$calendar = $this->createMock(ITestCalendar::class);
1600
-		$calendarData = clone $this->vCalendar3a;
1601
-		$calendarData->add('METHOD', 'CANCEL');
1602
-
1603
-		$this->time->expects(self::once())
1604
-			->method('getTime')
1605
-			->willReturn(1628374233);
1606
-		$manager->expects(self::once())
1607
-			->method('getCalendarsForPrincipal')
1608
-			->with($principalUri)
1609
-			->willReturn([$calendar]);
1610
-		$calendar->expects(self::once())
1611
-			->method('search')
1612
-			->willReturn([['uri' => 'testname.ics']]);
1613
-		$calendar->expects(self::once())
1614
-			->method('handleIMipMessage')
1615
-			->with('testname.ics', $calendarData->serialize());
1616
-		// Act
1617
-		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1618
-		// Assert
1619
-		$this->assertTrue($result);
1620
-	}
1621
-
1622
-	private function getFreeBusyResponse(): string {
1623
-		return <<<EOF
1552
+        $principalUri = 'principals/user/pierre';
1553
+        $sender = '[email protected]';
1554
+        $recipient = '[email protected]';
1555
+        $replyTo = '[email protected]';
1556
+        $calendar = $this->createMock(ITestCalendar::class);
1557
+        $calendarData = clone $this->vCalendar3a;
1558
+        $calendarData->add('METHOD', 'CANCEL');
1559
+
1560
+        $this->time->expects(self::once())
1561
+            ->method('getTime')
1562
+            ->willReturn(1628374233);
1563
+        $manager->expects(self::once())
1564
+            ->method('getCalendarsForPrincipal')
1565
+            ->with($principalUri)
1566
+            ->willReturn([$calendar]);
1567
+        $calendar->expects(self::once())
1568
+            ->method('search')
1569
+            ->willReturn([['uri' => 'testname.ics']]);
1570
+        $calendar->expects(self::once())
1571
+            ->method('handleIMipMessage')
1572
+            ->with('testname.ics', $calendarData->serialize());
1573
+        // Act
1574
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1575
+        // Assert
1576
+        $this->assertTrue($result);
1577
+    }
1578
+
1579
+    public function testHandleImipCancel(): void {
1580
+        /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
1581
+        $manager = $this->getMockBuilder(Manager::class)
1582
+            ->setConstructorArgs([
1583
+                $this->coordinator,
1584
+                $this->container,
1585
+                $this->logger,
1586
+                $this->time,
1587
+                $this->secureRandom,
1588
+                $this->userManager,
1589
+                $this->serverFactory,
1590
+            ])
1591
+            ->setMethods([
1592
+                'getCalendarsForPrincipal'
1593
+            ])
1594
+            ->getMock();
1595
+        $principalUri = 'principals/user/pierre';
1596
+        $sender = '[email protected]';
1597
+        $recipient = '[email protected]';
1598
+        $replyTo = null;
1599
+        $calendar = $this->createMock(ITestCalendar::class);
1600
+        $calendarData = clone $this->vCalendar3a;
1601
+        $calendarData->add('METHOD', 'CANCEL');
1602
+
1603
+        $this->time->expects(self::once())
1604
+            ->method('getTime')
1605
+            ->willReturn(1628374233);
1606
+        $manager->expects(self::once())
1607
+            ->method('getCalendarsForPrincipal')
1608
+            ->with($principalUri)
1609
+            ->willReturn([$calendar]);
1610
+        $calendar->expects(self::once())
1611
+            ->method('search')
1612
+            ->willReturn([['uri' => 'testname.ics']]);
1613
+        $calendar->expects(self::once())
1614
+            ->method('handleIMipMessage')
1615
+            ->with('testname.ics', $calendarData->serialize());
1616
+        // Act
1617
+        $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1618
+        // Assert
1619
+        $this->assertTrue($result);
1620
+    }
1621
+
1622
+    private function getFreeBusyResponse(): string {
1623
+        return <<<EOF
1624 1624
 <?xml version="1.0" encoding="utf-8"?>
1625 1625
 <cal:schedule-response xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
1626 1626
   <cal:response>
@@ -1698,139 +1698,139 @@  discard block
 block discarded – undo
1698 1698
   </cal:response>
1699 1699
 </cal:schedule-response>
1700 1700
 EOF;
1701
-	}
1702
-
1703
-	public function testCheckAvailability(): void {
1704
-		$organizer = $this->createMock(IUser::class);
1705
-		$organizer->expects(self::once())
1706
-			->method('getUID')
1707
-			->willReturn('admin');
1708
-		$organizer->expects(self::once())
1709
-			->method('getEMailAddress')
1710
-			->willReturn('[email protected]');
1711
-
1712
-		$user1 = $this->createMock(IUser::class);
1713
-		$user2 = $this->createMock(IUser::class);
1714
-
1715
-		$this->userManager->expects(self::exactly(3))
1716
-			->method('getByEmail')
1717
-			->willReturnMap([
1718
-				['[email protected]', [$user1]],
1719
-				['[email protected]', [$user2]],
1720
-				['[email protected]', []],
1721
-			]);
1722
-
1723
-		$authPlugin = $this->createMock(CustomPrincipalPlugin::class);
1724
-		$authPlugin->expects(self::once())
1725
-			->method('setCurrentPrincipal')
1726
-			->with('principals/users/admin');
1727
-
1728
-		$server = $this->createMock(\OCA\DAV\Connector\Sabre\Server::class);
1729
-		$server->expects(self::once())
1730
-			->method('getPlugin')
1731
-			->with('auth')
1732
-			->willReturn($authPlugin);
1733
-		$server->expects(self::once())
1734
-			->method('invokeMethod')
1735
-			->willReturnCallback(function (
1736
-				RequestInterface $request,
1737
-				ResponseInterface $response,
1738
-				bool $sendResponse,
1739
-			) {
1740
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1741
-				$this->assertEquals('POST', $request->getMethod());
1742
-				$this->assertEquals('calendars/admin/outbox', $request->getPath());
1743
-				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
1744
-				$this->assertEquals('0', $request->getHeader('Depth'));
1745
-				$this->assertEquals($requestBody, $request->getBodyAsString());
1746
-				$this->assertFalse($sendResponse);
1747
-				$response->setStatus(200);
1748
-				$response->setBody($this->getFreeBusyResponse());
1749
-			});
1750
-
1751
-		$this->serverFactory->expects(self::once())
1752
-			->method('createAttendeeAvailabilityServer')
1753
-			->willReturn($server);
1754
-
1755
-		$start = new DateTimeImmutable('2025-01-16T06:00:00Z');
1756
-		$end = new DateTimeImmutable('2025-01-17T06:00:00Z');
1757
-		$actual = $this->manager->checkAvailability($start, $end, $organizer, [
1758
-			'[email protected]',
1759
-			'[email protected]',
1760
-			'[email protected]',
1761
-		]);
1762
-		$expected = [
1763
-			new AvailabilityResult('[email protected]', false),
1764
-			new AvailabilityResult('[email protected]', true),
1765
-			new AvailabilityResult('[email protected]', false),
1766
-		];
1767
-		$this->assertEquals($expected, $actual);
1768
-	}
1769
-
1770
-	public function testCheckAvailabilityWithMailtoPrefix(): void {
1771
-		$organizer = $this->createMock(IUser::class);
1772
-		$organizer->expects(self::once())
1773
-			->method('getUID')
1774
-			->willReturn('admin');
1775
-		$organizer->expects(self::once())
1776
-			->method('getEMailAddress')
1777
-			->willReturn('[email protected]');
1778
-
1779
-		$user1 = $this->createMock(IUser::class);
1780
-		$user2 = $this->createMock(IUser::class);
1781
-
1782
-		$this->userManager->expects(self::exactly(3))
1783
-			->method('getByEmail')
1784
-			->willReturnMap([
1785
-				['[email protected]', [$user1]],
1786
-				['[email protected]', [$user2]],
1787
-				['[email protected]', []],
1788
-			]);
1789
-
1790
-		$authPlugin = $this->createMock(CustomPrincipalPlugin::class);
1791
-		$authPlugin->expects(self::once())
1792
-			->method('setCurrentPrincipal')
1793
-			->with('principals/users/admin');
1794
-
1795
-		$server = $this->createMock(\OCA\DAV\Connector\Sabre\Server::class);
1796
-		$server->expects(self::once())
1797
-			->method('getPlugin')
1798
-			->with('auth')
1799
-			->willReturn($authPlugin);
1800
-		$server->expects(self::once())
1801
-			->method('invokeMethod')
1802
-			->willReturnCallback(function (
1803
-				RequestInterface $request,
1804
-				ResponseInterface $response,
1805
-				bool $sendResponse,
1806
-			) {
1807
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1808
-				$this->assertEquals('POST', $request->getMethod());
1809
-				$this->assertEquals('calendars/admin/outbox', $request->getPath());
1810
-				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
1811
-				$this->assertEquals('0', $request->getHeader('Depth'));
1812
-				$this->assertEquals($requestBody, $request->getBodyAsString());
1813
-				$this->assertFalse($sendResponse);
1814
-				$response->setStatus(200);
1815
-				$response->setBody($this->getFreeBusyResponse());
1816
-			});
1817
-
1818
-		$this->serverFactory->expects(self::once())
1819
-			->method('createAttendeeAvailabilityServer')
1820
-			->willReturn($server);
1821
-
1822
-		$start = new DateTimeImmutable('2025-01-16T06:00:00Z');
1823
-		$end = new DateTimeImmutable('2025-01-17T06:00:00Z');
1824
-		$actual = $this->manager->checkAvailability($start, $end, $organizer, [
1825
-			'mailto:[email protected]',
1826
-			'mailto:[email protected]',
1827
-			'mailto:[email protected]',
1828
-		]);
1829
-		$expected = [
1830
-			new AvailabilityResult('[email protected]', false),
1831
-			new AvailabilityResult('[email protected]', true),
1832
-			new AvailabilityResult('[email protected]', false),
1833
-		];
1834
-		$this->assertEquals($expected, $actual);
1835
-	}
1701
+    }
1702
+
1703
+    public function testCheckAvailability(): void {
1704
+        $organizer = $this->createMock(IUser::class);
1705
+        $organizer->expects(self::once())
1706
+            ->method('getUID')
1707
+            ->willReturn('admin');
1708
+        $organizer->expects(self::once())
1709
+            ->method('getEMailAddress')
1710
+            ->willReturn('[email protected]');
1711
+
1712
+        $user1 = $this->createMock(IUser::class);
1713
+        $user2 = $this->createMock(IUser::class);
1714
+
1715
+        $this->userManager->expects(self::exactly(3))
1716
+            ->method('getByEmail')
1717
+            ->willReturnMap([
1718
+                ['[email protected]', [$user1]],
1719
+                ['[email protected]', [$user2]],
1720
+                ['[email protected]', []],
1721
+            ]);
1722
+
1723
+        $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
1724
+        $authPlugin->expects(self::once())
1725
+            ->method('setCurrentPrincipal')
1726
+            ->with('principals/users/admin');
1727
+
1728
+        $server = $this->createMock(\OCA\DAV\Connector\Sabre\Server::class);
1729
+        $server->expects(self::once())
1730
+            ->method('getPlugin')
1731
+            ->with('auth')
1732
+            ->willReturn($authPlugin);
1733
+        $server->expects(self::once())
1734
+            ->method('invokeMethod')
1735
+            ->willReturnCallback(function (
1736
+                RequestInterface $request,
1737
+                ResponseInterface $response,
1738
+                bool $sendResponse,
1739
+            ) {
1740
+                $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1741
+                $this->assertEquals('POST', $request->getMethod());
1742
+                $this->assertEquals('calendars/admin/outbox', $request->getPath());
1743
+                $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
1744
+                $this->assertEquals('0', $request->getHeader('Depth'));
1745
+                $this->assertEquals($requestBody, $request->getBodyAsString());
1746
+                $this->assertFalse($sendResponse);
1747
+                $response->setStatus(200);
1748
+                $response->setBody($this->getFreeBusyResponse());
1749
+            });
1750
+
1751
+        $this->serverFactory->expects(self::once())
1752
+            ->method('createAttendeeAvailabilityServer')
1753
+            ->willReturn($server);
1754
+
1755
+        $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
1756
+        $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
1757
+        $actual = $this->manager->checkAvailability($start, $end, $organizer, [
1758
+            '[email protected]',
1759
+            '[email protected]',
1760
+            '[email protected]',
1761
+        ]);
1762
+        $expected = [
1763
+            new AvailabilityResult('[email protected]', false),
1764
+            new AvailabilityResult('[email protected]', true),
1765
+            new AvailabilityResult('[email protected]', false),
1766
+        ];
1767
+        $this->assertEquals($expected, $actual);
1768
+    }
1769
+
1770
+    public function testCheckAvailabilityWithMailtoPrefix(): void {
1771
+        $organizer = $this->createMock(IUser::class);
1772
+        $organizer->expects(self::once())
1773
+            ->method('getUID')
1774
+            ->willReturn('admin');
1775
+        $organizer->expects(self::once())
1776
+            ->method('getEMailAddress')
1777
+            ->willReturn('[email protected]');
1778
+
1779
+        $user1 = $this->createMock(IUser::class);
1780
+        $user2 = $this->createMock(IUser::class);
1781
+
1782
+        $this->userManager->expects(self::exactly(3))
1783
+            ->method('getByEmail')
1784
+            ->willReturnMap([
1785
+                ['[email protected]', [$user1]],
1786
+                ['[email protected]', [$user2]],
1787
+                ['[email protected]', []],
1788
+            ]);
1789
+
1790
+        $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
1791
+        $authPlugin->expects(self::once())
1792
+            ->method('setCurrentPrincipal')
1793
+            ->with('principals/users/admin');
1794
+
1795
+        $server = $this->createMock(\OCA\DAV\Connector\Sabre\Server::class);
1796
+        $server->expects(self::once())
1797
+            ->method('getPlugin')
1798
+            ->with('auth')
1799
+            ->willReturn($authPlugin);
1800
+        $server->expects(self::once())
1801
+            ->method('invokeMethod')
1802
+            ->willReturnCallback(function (
1803
+                RequestInterface $request,
1804
+                ResponseInterface $response,
1805
+                bool $sendResponse,
1806
+            ) {
1807
+                $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1808
+                $this->assertEquals('POST', $request->getMethod());
1809
+                $this->assertEquals('calendars/admin/outbox', $request->getPath());
1810
+                $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
1811
+                $this->assertEquals('0', $request->getHeader('Depth'));
1812
+                $this->assertEquals($requestBody, $request->getBodyAsString());
1813
+                $this->assertFalse($sendResponse);
1814
+                $response->setStatus(200);
1815
+                $response->setBody($this->getFreeBusyResponse());
1816
+            });
1817
+
1818
+        $this->serverFactory->expects(self::once())
1819
+            ->method('createAttendeeAvailabilityServer')
1820
+            ->willReturn($server);
1821
+
1822
+        $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
1823
+        $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
1824
+        $actual = $this->manager->checkAvailability($start, $end, $organizer, [
1825
+            'mailto:[email protected]',
1826
+            'mailto:[email protected]',
1827
+            'mailto:[email protected]',
1828
+        ]);
1829
+        $expected = [
1830
+            new AvailabilityResult('[email protected]', false),
1831
+            new AvailabilityResult('[email protected]', true),
1832
+            new AvailabilityResult('[email protected]', false),
1833
+        ];
1834
+        $this->assertEquals($expected, $actual);
1835
+    }
1836 1836
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -1077,7 +1077,7 @@  discard block
 block discarded – undo
1077 1077
 		$calendarData->add('METHOD', 'REPLY');
1078 1078
 		// construct logger return
1079 1079
 		$this->logger->expects(self::once())->method('warning')
1080
-			->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1080
+			->with('iMip message event could not be processed because no corresponding event was found in any calendar '.$principalUri.'and UID'.$calendarData->VEVENT->UID->getValue());
1081 1081
 		// Act
1082 1082
 		$result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
1083 1083
 		// Assert
@@ -1525,7 +1525,7 @@  discard block
 block discarded – undo
1525 1525
 		$calendarData->add('METHOD', 'CANCEL');
1526 1526
 		// construct logger return
1527 1527
 		$this->logger->expects(self::once())->method('warning')
1528
-			->with('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $calendarData->VEVENT->UID->getValue());
1528
+			->with('iMip message event could not be processed because no corresponding event was found in any calendar '.$principalUri.'and UID'.$calendarData->VEVENT->UID->getValue());
1529 1529
 		// Act
1530 1530
 		$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
1531 1531
 		// Assert
@@ -1732,12 +1732,12 @@  discard block
 block discarded – undo
1732 1732
 			->willReturn($authPlugin);
1733 1733
 		$server->expects(self::once())
1734 1734
 			->method('invokeMethod')
1735
-			->willReturnCallback(function (
1735
+			->willReturnCallback(function(
1736 1736
 				RequestInterface $request,
1737 1737
 				ResponseInterface $response,
1738 1738
 				bool $sendResponse,
1739 1739
 			) {
1740
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1740
+				$requestBody = file_get_contents(__DIR__.'/../../data/ics/free-busy-request.ics');
1741 1741
 				$this->assertEquals('POST', $request->getMethod());
1742 1742
 				$this->assertEquals('calendars/admin/outbox', $request->getPath());
1743 1743
 				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
@@ -1799,12 +1799,12 @@  discard block
 block discarded – undo
1799 1799
 			->willReturn($authPlugin);
1800 1800
 		$server->expects(self::once())
1801 1801
 			->method('invokeMethod')
1802
-			->willReturnCallback(function (
1802
+			->willReturnCallback(function(
1803 1803
 				RequestInterface $request,
1804 1804
 				ResponseInterface $response,
1805 1805
 				bool $sendResponse,
1806 1806
 			) {
1807
-				$requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
1807
+				$requestBody = file_get_contents(__DIR__.'/../../data/ics/free-busy-request.ics');
1808 1808
 				$this->assertEquals('POST', $request->getMethod());
1809 1809
 				$this->assertEquals('calendars/admin/outbox', $request->getPath());
1810 1810
 				$this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
Please login to merge, or discard this patch.