1<?php
2
3namespace Sabre\CalDAV\Schedule;
4
5use Sabre\CalDAV;
6use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
7use Sabre\DAV;
8use Sabre\DAVACL;
9use Sabre\HTTP;
10
11class FreeBusyRequestTest extends \PHPUnit_Framework_TestCase {
12
13    protected $plugin;
14    protected $server;
15    protected $aclPlugin;
16    protected $request;
17    protected $authPlugin;
18    protected $caldavBackend;
19
20    function setUp() {
21
22        $caldavNS = '{' . CalDAV\Plugin::NS_CALDAV . '}';
23        $calendars = [
24            [
25                'principaluri'                  => 'principals/user2',
26                'id'                            => 1,
27                'uri'                           => 'calendar1',
28                $caldavNS . 'calendar-timezone' => "BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nEND:VTIMEZONE\r\nEND:VCALENDAR",
29            ],
30            [
31                'principaluri'                         => 'principals/user2',
32                'id'                                   => 2,
33                'uri'                                  => 'calendar2',
34                $caldavNS . 'schedule-calendar-transp' => new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT),
35            ],
36        ];
37        $calendarobjects = [
38            1 => ['1.ics' => [
39                'uri'          => '1.ics',
40                'calendardata' => 'BEGIN:VCALENDAR
41BEGIN:VEVENT
42DTSTART:20110101T130000
43DURATION:PT1H
44END:VEVENT
45END:VCALENDAR',
46                'calendarid' => 1,
47            ]],
48            2 => ['2.ics' => [
49                'uri'          => '2.ics',
50                'calendardata' => 'BEGIN:VCALENDAR
51BEGIN:VEVENT
52DTSTART:20110101T080000
53DURATION:PT1H
54END:VEVENT
55END:VCALENDAR',
56                'calendarid' => 2,
57            ]]
58
59        ];
60
61        $principalBackend = new DAVACL\PrincipalBackend\Mock();
62        $this->caldavBackend = new CalDAV\Backend\MockScheduling($calendars, $calendarobjects);
63
64        $tree = [
65            new DAVACL\PrincipalCollection($principalBackend),
66            new CalDAV\CalendarRoot($principalBackend, $this->caldavBackend),
67        ];
68
69        $this->request = HTTP\Sapi::createFromServerArray([
70            'CONTENT_TYPE' => 'text/calendar',
71        ]);
72        $this->response = new HTTP\ResponseMock();
73
74        $this->server = new DAV\Server($tree);
75        $this->server->httpRequest = $this->request;
76        $this->server->httpResponse = $this->response;
77
78        $this->aclPlugin = new DAVACL\Plugin();
79        $this->aclPlugin->allowUnauthenticatedAccess = false;
80        $this->server->addPlugin($this->aclPlugin);
81
82        $authBackend = new DAV\Auth\Backend\Mock();
83        $authBackend->setPrincipal('principals/user1');
84        $this->authPlugin = new DAV\Auth\Plugin($authBackend);
85        // Forcing authentication to work.
86        $this->authPlugin->beforeMethod($this->request, $this->response);
87        $this->server->addPlugin($this->authPlugin);
88
89        // CalDAV plugin
90        $this->plugin = new CalDAV\Plugin();
91        $this->server->addPlugin($this->plugin);
92
93        // Scheduling plugin
94        $this->plugin = new Plugin();
95        $this->server->addPlugin($this->plugin);
96
97    }
98
99    function testWrongContentType() {
100
101        $this->server->httpRequest = new HTTP\Request(
102            'POST',
103            '/calendars/user1/outbox',
104            ['Content-Type' => 'text/plain']
105        );
106
107        $this->assertNull(
108            $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse)
109        );
110
111    }
112
113    function testNotFound() {
114
115        $this->server->httpRequest = new HTTP\Request(
116            'POST',
117            '/calendars/user1/blabla',
118            ['Content-Type' => 'text/calendar']
119        );
120
121        $this->assertNull(
122            $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse)
123        );
124
125    }
126
127    function testNotOutbox() {
128
129        $this->server->httpRequest = new HTTP\Request(
130            'POST',
131            '/calendars/user1/inbox',
132            ['Content-Type' => 'text/calendar']
133        );
134
135        $this->assertNull(
136            $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse)
137        );
138
139    }
140
141    /**
142     * @expectedException Sabre\DAV\Exception\BadRequest
143     */
144    function testNoItipMethod() {
145
146        $this->server->httpRequest = new HTTP\Request(
147            'POST',
148            '/calendars/user1/outbox',
149            ['Content-Type' => 'text/calendar']
150        );
151
152        $body = <<<ICS
153BEGIN:VCALENDAR
154BEGIN:VFREEBUSY
155END:VFREEBUSY
156END:VCALENDAR
157ICS;
158
159        $this->server->httpRequest->setBody($body);
160        $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse);
161
162    }
163
164    /**
165     * @expectedException \Sabre\DAV\Exception\NotImplemented
166     */
167    function testNoVFreeBusy() {
168
169        $this->server->httpRequest = new HTTP\Request(
170            'POST',
171            '/calendars/user1/outbox',
172            ['Content-Type' => 'text/calendar']
173        );
174
175        $body = <<<ICS
176BEGIN:VCALENDAR
177METHOD:REQUEST
178BEGIN:VEVENT
179END:VEVENT
180END:VCALENDAR
181ICS;
182
183        $this->server->httpRequest->setBody($body);
184        $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse);
185
186    }
187
188    /**
189     * @expectedException Sabre\DAV\Exception\Forbidden
190     */
191    function testIncorrectOrganizer() {
192
193        $this->server->httpRequest = new HTTP\Request(
194            'POST',
195            '/calendars/user1/outbox',
196            ['Content-Type' => 'text/calendar']
197        );
198
199
200        $body = <<<ICS
201BEGIN:VCALENDAR
202METHOD:REQUEST
203BEGIN:VFREEBUSY
204ORGANIZER:mailto:john@wayne.org
205END:VFREEBUSY
206END:VCALENDAR
207ICS;
208
209        $this->server->httpRequest->setBody($body);
210        $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse);
211
212    }
213
214    /**
215     * @expectedException Sabre\DAV\Exception\BadRequest
216     */
217    function testNoAttendees() {
218
219        $this->server->httpRequest = new HTTP\Request(
220            'POST',
221            '/calendars/user1/outbox',
222            ['Content-Type' => 'text/calendar']
223        );
224
225        $body = <<<ICS
226BEGIN:VCALENDAR
227METHOD:REQUEST
228BEGIN:VFREEBUSY
229ORGANIZER:mailto:user1.sabredav@sabredav.org
230END:VFREEBUSY
231END:VCALENDAR
232ICS;
233
234        $this->server->httpRequest->setBody($body);
235        $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse);
236
237    }
238
239    /**
240     * @expectedException Sabre\DAV\Exception\BadRequest
241     */
242    function testNoDTStart() {
243
244        $this->server->httpRequest = new HTTP\Request(
245            'POST',
246            '/calendars/user1/outbox',
247            ['Content-Type' => 'text/calendar']
248        );
249
250        $body = <<<ICS
251BEGIN:VCALENDAR
252METHOD:REQUEST
253BEGIN:VFREEBUSY
254ORGANIZER:mailto:user1.sabredav@sabredav.org
255ATTENDEE:mailto:user2.sabredav@sabredav.org
256END:VFREEBUSY
257END:VCALENDAR
258ICS;
259
260        $this->server->httpRequest->setBody($body);
261        $this->plugin->httpPost($this->server->httpRequest, $this->server->httpResponse);
262
263    }
264
265    function testSucceed() {
266
267        $this->server->httpRequest = new HTTP\Request(
268            'POST',
269            '/calendars/user1/outbox',
270            ['Content-Type' => 'text/calendar']
271        );
272
273        $body = <<<ICS
274BEGIN:VCALENDAR
275METHOD:REQUEST
276BEGIN:VFREEBUSY
277ORGANIZER:mailto:user1.sabredav@sabredav.org
278ATTENDEE:mailto:user2.sabredav@sabredav.org
279ATTENDEE:mailto:user3.sabredav@sabredav.org
280DTSTART:20110101T080000Z
281DTEND:20110101T180000Z
282END:VFREEBUSY
283END:VCALENDAR
284ICS;
285
286        $this->server->httpRequest->setBody($body);
287
288        // Lazily making the current principal an admin.
289        $this->aclPlugin->adminPrincipals[] = 'principals/user1';
290
291        $this->assertFalse(
292            $this->plugin->httpPost($this->server->httpRequest, $this->response)
293        );
294
295        $this->assertEquals(200, $this->response->status);
296        $this->assertEquals([
297            'Content-Type' => ['application/xml'],
298        ], $this->response->getHeaders());
299
300        $strings = [
301            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
302            '<d:href>mailto:user3.sabredav@sabredav.org</d:href>',
303            '<cal:request-status>2.0;Success</cal:request-status>',
304            '<cal:request-status>3.7;Could not find principal</cal:request-status>',
305            'FREEBUSY:20110101T120000Z/20110101T130000Z',
306        ];
307
308        foreach ($strings as $string) {
309            $this->assertTrue(
310                strpos($this->response->body, $string) !== false,
311                'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body
312            );
313        }
314
315        $this->assertTrue(
316            strpos($this->response->body, 'FREEBUSY;FBTYPE=BUSY:20110101T080000Z/20110101T090000Z') == false,
317            'The response body did contain free busy info from a transparent calendar.'
318        );
319
320    }
321
322    /**
323     * Testing if the freebusy request still works, even if there are no
324     * calendars in the target users' account.
325     */
326    function testSucceedNoCalendars() {
327
328        // Deleting calendars
329        $this->caldavBackend->deleteCalendar(1);
330        $this->caldavBackend->deleteCalendar(2);
331
332        $this->server->httpRequest = new HTTP\Request(
333            'POST',
334            '/calendars/user1/outbox',
335            ['Content-Type' => 'text/calendar']
336        );
337
338        $body = <<<ICS
339BEGIN:VCALENDAR
340METHOD:REQUEST
341BEGIN:VFREEBUSY
342ORGANIZER:mailto:user1.sabredav@sabredav.org
343ATTENDEE:mailto:user2.sabredav@sabredav.org
344DTSTART:20110101T080000Z
345DTEND:20110101T180000Z
346END:VFREEBUSY
347END:VCALENDAR
348ICS;
349
350        $this->server->httpRequest->setBody($body);
351
352        // Lazily making the current principal an admin.
353        $this->aclPlugin->adminPrincipals[] = 'principals/user1';
354
355        $this->assertFalse(
356            $this->plugin->httpPost($this->server->httpRequest, $this->response)
357        );
358
359        $this->assertEquals(200, $this->response->status);
360        $this->assertEquals([
361            'Content-Type' => ['application/xml'],
362        ], $this->response->getHeaders());
363
364        $strings = [
365            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
366            '<cal:request-status>2.0;Success</cal:request-status>',
367        ];
368
369        foreach ($strings as $string) {
370            $this->assertTrue(
371                strpos($this->response->body, $string) !== false,
372                'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body
373            );
374        }
375
376    }
377
378    function testNoCalendarHomeFound() {
379
380        $this->server->httpRequest = new HTTP\Request(
381            'POST',
382            '/calendars/user1/outbox',
383            ['Content-Type' => 'text/calendar']
384        );
385
386        $body = <<<ICS
387BEGIN:VCALENDAR
388METHOD:REQUEST
389BEGIN:VFREEBUSY
390ORGANIZER:mailto:user1.sabredav@sabredav.org
391ATTENDEE:mailto:user2.sabredav@sabredav.org
392DTSTART:20110101T080000Z
393DTEND:20110101T180000Z
394END:VFREEBUSY
395END:VCALENDAR
396ICS;
397
398        $this->server->httpRequest->setBody($body);
399
400        // Lazily making the current principal an admin.
401        $this->aclPlugin->adminPrincipals[] = 'principals/user1';
402
403        // Removing the calendar home
404        $this->server->on('propFind', function(DAV\PropFind $propFind) {
405
406            $propFind->set('{' . Plugin::NS_CALDAV . '}calendar-home-set', null, 403);
407
408        });
409
410        $this->assertFalse(
411            $this->plugin->httpPost($this->server->httpRequest, $this->response)
412        );
413
414        $this->assertEquals(200, $this->response->status);
415        $this->assertEquals([
416            'Content-Type' => ['application/xml'],
417        ], $this->response->getHeaders());
418
419        $strings = [
420            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
421            '<cal:request-status>3.7;No calendar-home-set property found</cal:request-status>',
422        ];
423
424        foreach ($strings as $string) {
425            $this->assertTrue(
426                strpos($this->response->body, $string) !== false,
427                'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body
428            );
429        }
430
431    }
432
433    function testNoInboxFound() {
434
435        $this->server->httpRequest = new HTTP\Request(
436            'POST',
437            '/calendars/user1/outbox',
438            ['Content-Type' => 'text/calendar']
439        );
440
441        $body = <<<ICS
442BEGIN:VCALENDAR
443METHOD:REQUEST
444BEGIN:VFREEBUSY
445ORGANIZER:mailto:user1.sabredav@sabredav.org
446ATTENDEE:mailto:user2.sabredav@sabredav.org
447DTSTART:20110101T080000Z
448DTEND:20110101T180000Z
449END:VFREEBUSY
450END:VCALENDAR
451ICS;
452
453        $this->server->httpRequest->setBody($body);
454
455        // Lazily making the current principal an admin.
456        $this->aclPlugin->adminPrincipals[] = 'principals/user1';
457
458        // Removing the inbox
459        $this->server->on('propFind', function(DAV\PropFind $propFind) {
460
461            $propFind->set('{' . Plugin::NS_CALDAV . '}schedule-inbox-URL', null, 403);
462
463        });
464
465        $this->assertFalse(
466            $this->plugin->httpPost($this->server->httpRequest, $this->response)
467        );
468
469        $this->assertEquals(200, $this->response->status);
470        $this->assertEquals([
471            'Content-Type' => ['application/xml'],
472        ], $this->response->getHeaders());
473
474        $strings = [
475            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
476            '<cal:request-status>3.7;No schedule-inbox-URL property found</cal:request-status>',
477        ];
478
479        foreach ($strings as $string) {
480            $this->assertTrue(
481                strpos($this->response->body, $string) !== false,
482                'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body
483            );
484        }
485
486    }
487
488    function testSucceedUseVAVAILABILITY() {
489
490        $this->server->httpRequest = new HTTP\Request(
491            'POST',
492            '/calendars/user1/outbox',
493            ['Content-Type' => 'text/calendar']
494        );
495
496        $body = <<<ICS
497BEGIN:VCALENDAR
498METHOD:REQUEST
499BEGIN:VFREEBUSY
500ORGANIZER:mailto:user1.sabredav@sabredav.org
501ATTENDEE:mailto:user2.sabredav@sabredav.org
502DTSTART:20110101T080000Z
503DTEND:20110101T180000Z
504END:VFREEBUSY
505END:VCALENDAR
506ICS;
507
508        $this->server->httpRequest->setBody($body);
509
510        // Lazily making the current principal an admin.
511        $this->aclPlugin->adminPrincipals[] = 'principals/user1';
512
513        // Adding VAVAILABILITY manually
514        $this->server->on('propFind', function(DAV\PropFind $propFind) {
515
516            $propFind->handle('{' . Plugin::NS_CALDAV . '}calendar-availability', function() {
517
518                $avail = <<<ICS
519BEGIN:VCALENDAR
520BEGIN:VAVAILABILITY
521DTSTART:20110101T000000Z
522DTEND:20110102T000000Z
523BEGIN:AVAILABLE
524DTSTART:20110101T090000Z
525DTEND:20110101T170000Z
526END:AVAILABLE
527END:VAVAILABILITY
528END:VCALENDAR
529ICS;
530                return $avail;
531
532            });
533
534        });
535
536        $this->assertFalse(
537            $this->plugin->httpPost($this->server->httpRequest, $this->response)
538        );
539
540        $this->assertEquals(200, $this->response->status);
541        $this->assertEquals([
542            'Content-Type' => ['application/xml'],
543        ], $this->response->getHeaders());
544
545        $strings = [
546            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
547            '<cal:request-status>2.0;Success</cal:request-status>',
548            'FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T080000Z/20110101T090000Z',
549            'FREEBUSY:20110101T120000Z/20110101T130000Z',
550            'FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T170000Z/20110101T180000Z',
551        ];
552
553        foreach ($strings as $string) {
554            $this->assertTrue(
555                strpos($this->response->body, $string) !== false,
556                'The response body did not contain: ' . $string . 'Full response: ' . $this->response->body
557            );
558        }
559
560    }
561
562    /*
563    function testNoPrivilege() {
564
565        $this->markTestIncomplete('Currently there\'s no "no privilege" situation');
566
567        $this->server->httpRequest = HTTP\Sapi::createFromServerArray(array(
568            'CONTENT_TYPE' => 'text/calendar',
569            'REQUEST_METHOD' => 'POST',
570            'REQUEST_URI' => '/calendars/user1/outbox',
571        ));
572
573        $body = <<<ICS
574BEGIN:VCALENDAR
575METHOD:REQUEST
576BEGIN:VFREEBUSY
577ORGANIZER:mailto:user1.sabredav@sabredav.org
578ATTENDEE:mailto:user2.sabredav@sabredav.org
579DTSTART:20110101T080000Z
580DTEND:20110101T180000Z
581END:VFREEBUSY
582END:VCALENDAR
583ICS;
584
585        $this->server->httpRequest->setBody($body);
586
587        $this->assertFalse(
588            $this->plugin->httpPost($this->server->httpRequest, $this->response)
589        );
590
591        $this->assertEquals(200, $this->response->status);
592        $this->assertEquals([
593            'Content-Type' => 'application/xml',
594        ], $this->response->getHeaders());
595
596        $strings = [
597            '<d:href>mailto:user2.sabredav@sabredav.org</d:href>',
598            '<cal:request-status>3.7;No calendar-home-set property found</cal:request-status>',
599        ];
600
601        foreach($strings as $string) {
602            $this->assertTrue(
603                strpos($this->response->body, $string)!==false,
604                'The response body did not contain: ' . $string .'Full response: ' . $this->response->body
605            );
606        }
607
608
609    }*/
610
611}
612