1<?php 2 3namespace Sabre\CalDAV; 4 5use Sabre\DAV; 6use Sabre\DAVACL; 7use Sabre\HTTP; 8 9require_once 'Sabre/HTTP/ResponseMock.php'; 10 11class ValidateICalTest extends \PHPUnit_Framework_TestCase { 12 13 /** 14 * @var Sabre\DAV\Server 15 */ 16 protected $server; 17 /** 18 * @var Sabre\CalDAV\Backend\Mock 19 */ 20 protected $calBackend; 21 22 function setUp() { 23 24 $calendars = [ 25 [ 26 'id' => 'calendar1', 27 'principaluri' => 'principals/admin', 28 'uri' => 'calendar1', 29 '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VEVENT', 'VTODO', 'VJOURNAL']), 30 ], 31 [ 32 'id' => 'calendar2', 33 'principaluri' => 'principals/admin', 34 'uri' => 'calendar2', 35 '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => new Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VJOURNAL']), 36 ] 37 ]; 38 39 $this->calBackend = new Backend\Mock($calendars, []); 40 $principalBackend = new DAVACL\PrincipalBackend\Mock(); 41 42 $tree = [ 43 new CalendarRoot($principalBackend, $this->calBackend), 44 ]; 45 46 $this->server = new DAV\Server($tree); 47 $this->server->sapi = new HTTP\SapiMock(); 48 $this->server->debugExceptions = true; 49 50 $plugin = new Plugin(); 51 $this->server->addPlugin($plugin); 52 53 $response = new HTTP\ResponseMock(); 54 $this->server->httpResponse = $response; 55 56 } 57 58 function request(HTTP\Request $request) { 59 60 $this->server->httpRequest = $request; 61 $this->server->exec(); 62 63 return $this->server->httpResponse; 64 65 } 66 67 function testCreateFile() { 68 69 $request = HTTP\Sapi::createFromServerArray([ 70 'REQUEST_METHOD' => 'PUT', 71 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 72 ]); 73 74 $response = $this->request($request); 75 76 $this->assertEquals(415, $response->status); 77 78 } 79 80 function testCreateFileValid() { 81 82 $request = new HTTP\Request( 83 'PUT', 84 '/calendars/admin/calendar1/blabla.ics', 85 ['Prefer' => 'handling=strict'] 86 ); 87 88 $ics = <<<ICS 89BEGIN:VCALENDAR 90VERSION:2.0 91PRODID:foo 92BEGIN:VEVENT 93UID:foo 94DTSTAMP:20160406T052348Z 95DTSTART:20160706T140000Z 96END:VEVENT 97END:VCALENDAR 98ICS; 99 100 $request->setBody($ics); 101 102 $response = $this->request($request); 103 104 $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 105 $this->assertEquals([ 106 'X-Sabre-Version' => [DAV\Version::VERSION], 107 'Content-Length' => ['0'], 108 'ETag' => ['"' . md5($ics) . '"'], 109 ], $response->getHeaders()); 110 111 $expected = [ 112 'uri' => 'blabla.ics', 113 'calendardata' => $ics, 114 'calendarid' => 'calendar1', 115 'lastmodified' => null, 116 ]; 117 118 $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); 119 120 } 121 122 function testCreateFileNoVersion() { 123 124 $request = new HTTP\Request( 125 'PUT', 126 '/calendars/admin/calendar1/blabla.ics', 127 ['Prefer' => 'handling=strict'] 128 ); 129 130 $ics = <<<ICS 131BEGIN:VCALENDAR 132PRODID:foo 133BEGIN:VEVENT 134UID:foo 135DTSTAMP:20160406T052348Z 136DTSTART:20160706T140000Z 137END:VEVENT 138END:VCALENDAR 139ICS; 140 141 $request->setBody($ics); 142 143 $response = $this->request($request); 144 145 $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 146 147 } 148 149 function testCreateFileNoVersionFixed() { 150 151 $request = new HTTP\Request( 152 'PUT', 153 '/calendars/admin/calendar1/blabla.ics', 154 ['Prefer' => 'handling=lenient'] 155 ); 156 157 $ics = <<<ICS 158BEGIN:VCALENDAR 159PRODID:foo 160BEGIN:VEVENT 161UID:foo 162DTSTAMP:20160406T052348Z 163DTSTART:20160706T140000Z 164END:VEVENT 165END:VCALENDAR 166ICS; 167 168 $request->setBody($ics); 169 170 $response = $this->request($request); 171 172 $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 173 $this->assertEquals([ 174 'X-Sabre-Version' => [DAV\Version::VERSION], 175 'Content-Length' => ['0'], 176 'X-Sabre-Ew-Gross' => ['iCalendar validation warning: VERSION MUST appear exactly once in a VCALENDAR component'], 177 ], $response->getHeaders()); 178 179 $ics = <<<ICS 180BEGIN:VCALENDAR\r 181VERSION:2.0\r 182PRODID:foo\r 183BEGIN:VEVENT\r 184UID:foo\r 185DTSTAMP:20160406T052348Z\r 186DTSTART:20160706T140000Z\r 187END:VEVENT\r 188END:VCALENDAR\r 189 190ICS; 191 192 $expected = [ 193 'uri' => 'blabla.ics', 194 'calendardata' => $ics, 195 'calendarid' => 'calendar1', 196 'lastmodified' => null, 197 ]; 198 199 $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); 200 201 } 202 203 function testCreateFileNoComponents() { 204 205 $request = new HTTP\Request( 206 'PUT', 207 '/calendars/admin/calendar1/blabla.ics', 208 ['Prefer' => 'handling=strict'] 209 ); 210 $ics = <<<ICS 211BEGIN:VCALENDAR 212VERSION:2.0 213PRODID:foo 214END:VCALENDAR 215ICS; 216 217 $request->setBody($ics); 218 219 $response = $this->request($request); 220 $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 221 222 } 223 224 function testCreateFileNoUID() { 225 226 $request = HTTP\Sapi::createFromServerArray([ 227 'REQUEST_METHOD' => 'PUT', 228 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 229 ]); 230 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); 231 232 $response = $this->request($request); 233 234 $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 235 236 } 237 238 function testCreateFileVCard() { 239 240 $request = HTTP\Sapi::createFromServerArray([ 241 'REQUEST_METHOD' => 'PUT', 242 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 243 ]); 244 $request->setBody("BEGIN:VCARD\r\nEND:VCARD\r\n"); 245 246 $response = $this->request($request); 247 248 $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 249 250 } 251 252 function testCreateFile2Components() { 253 254 $request = HTTP\Sapi::createFromServerArray([ 255 'REQUEST_METHOD' => 'PUT', 256 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 257 ]); 258 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VJOURNAL\r\nUID:foo\r\nEND:VJOURNAL\r\nEND:VCALENDAR\r\n"); 259 260 $response = $this->request($request); 261 262 $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 263 264 } 265 266 function testCreateFile2UIDS() { 267 268 $request = HTTP\Sapi::createFromServerArray([ 269 'REQUEST_METHOD' => 'PUT', 270 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 271 ]); 272 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nBEGIN:VEVENT\r\nUID:bar\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); 273 274 $response = $this->request($request); 275 276 $this->assertEquals(415, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 277 278 } 279 280 function testCreateFileWrongComponent() { 281 282 $request = HTTP\Sapi::createFromServerArray([ 283 'REQUEST_METHOD' => 'PUT', 284 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 285 ]); 286 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VFREEBUSY\r\nUID:foo\r\nEND:VFREEBUSY\r\nEND:VCALENDAR\r\n"); 287 288 $response = $this->request($request); 289 290 $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 291 292 } 293 294 function testUpdateFile() { 295 296 $this->calBackend->createCalendarObject('calendar1', 'blabla.ics', 'foo'); 297 $request = HTTP\Sapi::createFromServerArray([ 298 'REQUEST_METHOD' => 'PUT', 299 'REQUEST_URI' => '/calendars/admin/calendar1/blabla.ics', 300 ]); 301 302 $response = $this->request($request); 303 304 $this->assertEquals(415, $response->status); 305 306 } 307 308 function testUpdateFileParsableBody() { 309 310 $this->calBackend->createCalendarObject('calendar1', 'blabla.ics', 'foo'); 311 $request = new HTTP\Request( 312 'PUT', 313 '/calendars/admin/calendar1/blabla.ics' 314 ); 315 $ics = <<<ICS 316BEGIN:VCALENDAR 317VERSION:2.0 318PRODID:foo 319BEGIN:VEVENT 320UID:foo 321DTSTAMP:20160406T052348Z 322DTSTART:20160706T140000Z 323END:VEVENT 324END:VCALENDAR 325ICS; 326 327 $request->setBody($ics); 328 $response = $this->request($request); 329 330 $this->assertEquals(204, $response->status); 331 332 $expected = [ 333 'uri' => 'blabla.ics', 334 'calendardata' => $ics, 335 'calendarid' => 'calendar1', 336 'lastmodified' => null, 337 ]; 338 339 $this->assertEquals($expected, $this->calBackend->getCalendarObject('calendar1', 'blabla.ics')); 340 341 } 342 343 function testCreateFileInvalidComponent() { 344 345 $request = HTTP\Sapi::createFromServerArray([ 346 'REQUEST_METHOD' => 'PUT', 347 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', 348 ]); 349 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); 350 351 $response = $this->request($request); 352 353 $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 354 355 } 356 357 function testUpdateFileInvalidComponent() { 358 359 $this->calBackend->createCalendarObject('calendar2', 'blabla.ics', 'foo'); 360 $request = HTTP\Sapi::createFromServerArray([ 361 'REQUEST_METHOD' => 'PUT', 362 'REQUEST_URI' => '/calendars/admin/calendar2/blabla.ics', 363 ]); 364 $request->setBody("BEGIN:VCALENDAR\r\nBEGIN:VTIMEZONE\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nUID:foo\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); 365 366 $response = $this->request($request); 367 368 $this->assertEquals(403, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 369 370 } 371 372 /** 373 * What we are testing here, is if we send in a latin1 character, the 374 * server should automatically transform this into UTF-8. 375 * 376 * More importantly. If any transformation happens, the etag must no longer 377 * be returned by the server. 378 */ 379 function testCreateFileModified() { 380 381 $request = new HTTP\Request( 382 'PUT', 383 '/calendars/admin/calendar1/blabla.ics' 384 ); 385 $ics = <<<ICS 386BEGIN:VCALENDAR 387VERSION:2.0 388PRODID:foo 389BEGIN:VEVENT 390UID:foo 391SUMMARY:Meeting in M\xfcnster 392DTSTAMP:20160406T052348Z 393DTSTART:20160706T140000Z 394END:VEVENT 395END:VCALENDAR 396ICS; 397 398 $request->setBody($ics); 399 400 $response = $this->request($request); 401 402 $this->assertEquals(201, $response->status, 'Incorrect status returned! Full response body: ' . $response->body); 403 $this->assertNull($response->getHeader('ETag')); 404 405 } 406} 407