1<?php
2
3namespace Sabre\DAV\Locks;
4
5use Sabre\DAV;
6use Sabre\HTTP;
7
8require_once 'Sabre/DAV/AbstractServer.php';
9
10class PluginTest extends DAV\AbstractServer {
11
12    /**
13     * @var Plugin
14     */
15    protected $locksPlugin;
16
17    function setUp() {
18
19        parent::setUp();
20        $locksBackend = new Backend\File(SABRE_TEMPDIR . '/locksdb');
21        $locksPlugin = new Plugin($locksBackend);
22        $this->server->addPlugin($locksPlugin);
23        $this->locksPlugin = $locksPlugin;
24
25    }
26
27    function testGetInfo() {
28
29        $this->assertArrayHasKey(
30            'name',
31            $this->locksPlugin->getPluginInfo()
32        );
33
34    }
35
36    function testGetFeatures() {
37
38        $this->assertEquals([2], $this->locksPlugin->getFeatures());
39
40    }
41
42    function testGetHTTPMethods() {
43
44        $this->assertEquals(['LOCK', 'UNLOCK'], $this->locksPlugin->getHTTPMethods(''));
45
46    }
47
48    function testLockNoBody() {
49
50        $request = new HTTP\Request('LOCK', '/test.txt');
51        $this->server->httpRequest = $request;
52        $this->server->exec();
53
54        $this->assertEquals([
55            'X-Sabre-Version' => [DAV\Version::VERSION],
56            'Content-Type'    => ['application/xml; charset=utf-8'],
57            ],
58            $this->response->getHeaders()
59         );
60
61        $this->assertEquals(400, $this->response->status);
62
63    }
64
65    function testLock() {
66
67        $request = new HTTP\Request('LOCK', '/test.txt');
68        $request->setBody('<?xml version="1.0"?>
69<D:lockinfo xmlns:D="DAV:">
70    <D:lockscope><D:exclusive/></D:lockscope>
71    <D:locktype><D:write/></D:locktype>
72    <D:owner>
73        <D:href>http://example.org/~ejw/contact.html</D:href>
74    </D:owner>
75</D:lockinfo>');
76
77        $this->server->httpRequest = $request;
78        $this->server->exec();
79
80        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
81        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
82
83        $this->assertEquals(200, $this->response->status, 'Got an incorrect status back. Response body: ' . $this->response->body);
84
85        $body = preg_replace("/xmlns(:[A-Za-z0-9_])?=(\"|\')DAV:(\"|\')/", "xmlns\\1=\"urn:DAV\"", $this->response->body);
86        $xml = simplexml_load_string($body);
87        $xml->registerXPathNamespace('d', 'urn:DAV');
88
89        $elements = [
90            '/d:prop',
91            '/d:prop/d:lockdiscovery',
92            '/d:prop/d:lockdiscovery/d:activelock',
93            '/d:prop/d:lockdiscovery/d:activelock/d:locktype',
94            '/d:prop/d:lockdiscovery/d:activelock/d:lockroot',
95            '/d:prop/d:lockdiscovery/d:activelock/d:lockroot/d:href',
96            '/d:prop/d:lockdiscovery/d:activelock/d:locktype/d:write',
97            '/d:prop/d:lockdiscovery/d:activelock/d:lockscope',
98            '/d:prop/d:lockdiscovery/d:activelock/d:lockscope/d:exclusive',
99            '/d:prop/d:lockdiscovery/d:activelock/d:depth',
100            '/d:prop/d:lockdiscovery/d:activelock/d:owner',
101            '/d:prop/d:lockdiscovery/d:activelock/d:timeout',
102            '/d:prop/d:lockdiscovery/d:activelock/d:locktoken',
103            '/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href',
104        ];
105
106        foreach ($elements as $elem) {
107            $data = $xml->xpath($elem);
108            $this->assertEquals(1, count($data), 'We expected 1 match for the xpath expression "' . $elem . '". ' . count($data) . ' were found. Full response body: ' . $this->response->body);
109        }
110
111        $depth = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:depth');
112        $this->assertEquals('infinity', (string)$depth[0]);
113
114        $token = $xml->xpath('/d:prop/d:lockdiscovery/d:activelock/d:locktoken/d:href');
115        $this->assertEquals($this->response->getHeader('Lock-Token'), '<' . (string)$token[0] . '>', 'Token in response body didn\'t match token in response header.');
116
117    }
118
119    /**
120     * @depends testLock
121     */
122    function testDoubleLock() {
123
124        $request = new HTTP\Request('LOCK', '/test.txt');
125        $request->setBody('<?xml version="1.0"?>
126<D:lockinfo xmlns:D="DAV:">
127    <D:lockscope><D:exclusive/></D:lockscope>
128    <D:locktype><D:write/></D:locktype>
129    <D:owner>
130        <D:href>http://example.org/~ejw/contact.html</D:href>
131    </D:owner>
132</D:lockinfo>');
133
134        $this->server->httpRequest = $request;
135        $this->server->exec();
136
137        $this->response = new HTTP\ResponseMock();
138        $this->server->httpResponse = $this->response;
139
140        $this->server->exec();
141
142        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
143
144        $this->assertEquals(423, $this->response->status, 'Full response: ' . $this->response->body);
145
146    }
147
148    /**
149     * @depends testLock
150     */
151    function testLockRefresh() {
152
153        $request = new HTTP\Request('LOCK', '/test.txt');
154        $request->setBody('<?xml version="1.0"?>
155<D:lockinfo xmlns:D="DAV:">
156    <D:lockscope><D:exclusive/></D:lockscope>
157    <D:locktype><D:write/></D:locktype>
158    <D:owner>
159        <D:href>http://example.org/~ejw/contact.html</D:href>
160    </D:owner>
161</D:lockinfo>');
162
163        $this->server->httpRequest = $request;
164        $this->server->exec();
165
166        $lockToken = $this->response->getHeader('Lock-Token');
167
168        $this->response = new HTTP\ResponseMock();
169        $this->server->httpResponse = $this->response;
170
171        $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . ')']);
172        $request->setBody('');
173
174        $this->server->httpRequest = $request;
175        $this->server->exec();
176
177        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
178
179        $this->assertEquals(200, $this->response->status, 'We received an incorrect status code. Full response body: ' . $this->response->getBody());
180
181    }
182
183    /**
184     * @depends testLock
185     */
186    function testLockRefreshBadToken() {
187
188        $request = new HTTP\Request('LOCK', '/test.txt');
189        $request->setBody('<?xml version="1.0"?>
190<D:lockinfo xmlns:D="DAV:">
191    <D:lockscope><D:exclusive/></D:lockscope>
192    <D:locktype><D:write/></D:locktype>
193    <D:owner>
194        <D:href>http://example.org/~ejw/contact.html</D:href>
195    </D:owner>
196</D:lockinfo>');
197
198        $this->server->httpRequest = $request;
199        $this->server->exec();
200
201        $lockToken = $this->response->getHeader('Lock-Token');
202
203        $this->response = new HTTP\ResponseMock();
204        $this->server->httpResponse = $this->response;
205
206        $request = new HTTP\Request('LOCK', '/test.txt', ['If' => '(' . $lockToken . 'foobar) (<opaquelocktoken:anotherbadtoken>)']);
207        $request->setBody('');
208
209        $this->server->httpRequest = $request;
210        $this->server->exec();
211
212        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
213
214        $this->assertEquals(423, $this->response->getStatus(), 'We received an incorrect status code. Full response body: ' . $this->response->getBody());
215
216    }
217
218    /**
219     * @depends testLock
220     */
221    function testLockNoFile() {
222
223        $request = new HTTP\Request('LOCK', '/notfound.txt');
224        $request->setBody('<?xml version="1.0"?>
225<D:lockinfo xmlns:D="DAV:">
226    <D:lockscope><D:exclusive/></D:lockscope>
227    <D:locktype><D:write/></D:locktype>
228    <D:owner>
229        <D:href>http://example.org/~ejw/contact.html</D:href>
230    </D:owner>
231</D:lockinfo>');
232
233        $this->server->httpRequest = $request;
234        $this->server->exec();
235
236        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
237        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
238
239        $this->assertEquals(201, $this->response->status);
240
241    }
242
243    /**
244     * @depends testLock
245     */
246    function testUnlockNoToken() {
247
248        $request = new HTTP\Request('UNLOCK', '/test.txt');
249        $this->server->httpRequest = $request;
250        $this->server->exec();
251
252        $this->assertEquals([
253            'X-Sabre-Version' => [DAV\Version::VERSION],
254            'Content-Type'    => ['application/xml; charset=utf-8'],
255            ],
256            $this->response->getHeaders()
257         );
258
259        $this->assertEquals(400, $this->response->status);
260
261    }
262
263    /**
264     * @depends testLock
265     */
266    function testUnlockBadToken() {
267
268        $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => '<opaquelocktoken:blablabla>']);
269        $this->server->httpRequest = $request;
270        $this->server->exec();
271
272        $this->assertEquals([
273            'X-Sabre-Version' => [DAV\Version::VERSION],
274            'Content-Type'    => ['application/xml; charset=utf-8'],
275            ],
276            $this->response->getHeaders()
277         );
278
279        $this->assertEquals(409, $this->response->status, 'Got an incorrect status code. Full response body: ' . $this->response->body);
280
281    }
282
283    /**
284     * @depends testLock
285     */
286    function testLockPutNoToken() {
287
288        $request = new HTTP\Request('LOCK', '/test.txt');
289        $request->setBody('<?xml version="1.0"?>
290<D:lockinfo xmlns:D="DAV:">
291    <D:lockscope><D:exclusive/></D:lockscope>
292    <D:locktype><D:write/></D:locktype>
293    <D:owner>
294        <D:href>http://example.org/~ejw/contact.html</D:href>
295    </D:owner>
296</D:lockinfo>');
297
298        $this->server->httpRequest = $request;
299        $this->server->exec();
300
301        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
302        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
303
304        $this->assertEquals(200, $this->response->status);
305
306        $request = new HTTP\Request('PUT', '/test.txt');
307        $request->setBody('newbody');
308        $this->server->httpRequest = $request;
309        $this->server->exec();
310
311        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
312        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
313
314        $this->assertEquals(423, $this->response->status);
315
316    }
317
318    /**
319     * @depends testLock
320     */
321    function testUnlock() {
322
323        $request = new HTTP\Request('LOCK', '/test.txt');
324        $this->server->httpRequest = $request;
325
326        $request->setBody('<?xml version="1.0"?>
327<D:lockinfo xmlns:D="DAV:">
328    <D:lockscope><D:exclusive/></D:lockscope>
329    <D:locktype><D:write/></D:locktype>
330    <D:owner>
331        <D:href>http://example.org/~ejw/contact.html</D:href>
332    </D:owner>
333</D:lockinfo>');
334
335        $this->server->invokeMethod($request, $this->server->httpResponse);
336        $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
337
338        $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]);
339        $this->server->httpRequest = $request;
340        $this->server->httpResponse = new HTTP\ResponseMock();
341        $this->server->invokeMethod($request, $this->server->httpResponse);
342
343        $this->assertEquals(204, $this->server->httpResponse->status, 'Got an incorrect status code. Full response body: ' . $this->response->body);
344        $this->assertEquals([
345            'X-Sabre-Version' => [DAV\Version::VERSION],
346            'Content-Length'  => ['0'],
347            ],
348            $this->server->httpResponse->getHeaders()
349         );
350
351
352    }
353
354    /**
355     * @depends testLock
356     */
357    function testUnlockWindowsBug() {
358
359        $request = new HTTP\Request('LOCK', '/test.txt');
360        $this->server->httpRequest = $request;
361
362        $request->setBody('<?xml version="1.0"?>
363<D:lockinfo xmlns:D="DAV:">
364    <D:lockscope><D:exclusive/></D:lockscope>
365    <D:locktype><D:write/></D:locktype>
366    <D:owner>
367        <D:href>http://example.org/~ejw/contact.html</D:href>
368    </D:owner>
369</D:lockinfo>');
370
371        $this->server->invokeMethod($request, $this->server->httpResponse);
372        $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
373
374        // See Issue 123
375        $lockToken = trim($lockToken, '<>');
376
377        $request = new HTTP\Request('UNLOCK', '/test.txt', ['Lock-Token' => $lockToken]);
378        $this->server->httpRequest = $request;
379        $this->server->httpResponse = new HTTP\ResponseMock();
380        $this->server->invokeMethod($request, $this->server->httpResponse);
381
382        $this->assertEquals(204, $this->server->httpResponse->status, 'Got an incorrect status code. Full response body: ' . $this->response->body);
383        $this->assertEquals([
384            'X-Sabre-Version' => [DAV\Version::VERSION],
385            'Content-Length'  => ['0'],
386            ],
387            $this->server->httpResponse->getHeaders()
388         );
389
390
391    }
392
393    /**
394     * @depends testLock
395     */
396    function testLockRetainOwner() {
397
398        $request = HTTP\Sapi::createFromServerArray([
399            'REQUEST_URI'    => '/test.txt',
400            'REQUEST_METHOD' => 'LOCK',
401        ]);
402        $this->server->httpRequest = $request;
403
404        $request->setBody('<?xml version="1.0"?>
405<D:lockinfo xmlns:D="DAV:">
406    <D:lockscope><D:exclusive/></D:lockscope>
407    <D:locktype><D:write/></D:locktype>
408    <D:owner>Evert</D:owner>
409</D:lockinfo>');
410
411        $this->server->invokeMethod($request, $this->server->httpResponse);
412        $lockToken = $this->server->httpResponse->getHeader('Lock-Token');
413
414        $locks = $this->locksPlugin->getLocks('test.txt');
415        $this->assertEquals(1, count($locks));
416        $this->assertEquals('Evert', $locks[0]->owner);
417
418
419    }
420
421    /**
422     * @depends testLock
423     */
424    function testLockPutBadToken() {
425
426        $serverVars = [
427            'REQUEST_URI'    => '/test.txt',
428            'REQUEST_METHOD' => 'LOCK',
429        ];
430
431        $request = HTTP\Sapi::createFromServerArray($serverVars);
432        $request->setBody('<?xml version="1.0"?>
433<D:lockinfo xmlns:D="DAV:">
434    <D:lockscope><D:exclusive/></D:lockscope>
435    <D:locktype><D:write/></D:locktype>
436    <D:owner>
437        <D:href>http://example.org/~ejw/contact.html</D:href>
438    </D:owner>
439</D:lockinfo>');
440
441        $this->server->httpRequest = $request;
442        $this->server->exec();
443
444        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
445        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
446
447        $this->assertEquals(200, $this->response->status);
448
449        $serverVars = [
450            'REQUEST_URI'    => '/test.txt',
451            'REQUEST_METHOD' => 'PUT',
452            'HTTP_IF'        => '(<opaquelocktoken:token1>)',
453        ];
454
455        $request = HTTP\Sapi::createFromServerArray($serverVars);
456        $request->setBody('newbody');
457        $this->server->httpRequest = $request;
458        $this->server->exec();
459
460        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
461        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
462
463        // $this->assertEquals('412 Precondition failed',$this->response->status);
464        $this->assertEquals(423, $this->response->status);
465
466    }
467
468    /**
469     * @depends testLock
470     */
471    function testLockDeleteParent() {
472
473        $serverVars = [
474            'REQUEST_URI'    => '/dir/child.txt',
475            'REQUEST_METHOD' => 'LOCK',
476        ];
477
478        $request = HTTP\Sapi::createFromServerArray($serverVars);
479        $request->setBody('<?xml version="1.0"?>
480<D:lockinfo xmlns:D="DAV:">
481    <D:lockscope><D:exclusive/></D:lockscope>
482    <D:locktype><D:write/></D:locktype>
483    <D:owner>
484        <D:href>http://example.org/~ejw/contact.html</D:href>
485    </D:owner>
486</D:lockinfo>');
487
488        $this->server->httpRequest = $request;
489        $this->server->exec();
490
491        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
492        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
493
494        $this->assertEquals(200, $this->response->status);
495
496        $serverVars = [
497            'REQUEST_URI'    => '/dir',
498            'REQUEST_METHOD' => 'DELETE',
499        ];
500
501        $request = HTTP\Sapi::createFromServerArray($serverVars);
502        $this->server->httpRequest = $request;
503        $this->server->exec();
504
505        $this->assertEquals(423, $this->response->status);
506        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
507
508    }
509    /**
510     * @depends testLock
511     */
512    function testLockDeleteSucceed() {
513
514        $serverVars = [
515            'REQUEST_URI'    => '/dir/child.txt',
516            'REQUEST_METHOD' => 'LOCK',
517        ];
518
519        $request = HTTP\Sapi::createFromServerArray($serverVars);
520        $request->setBody('<?xml version="1.0"?>
521<D:lockinfo xmlns:D="DAV:">
522    <D:lockscope><D:exclusive/></D:lockscope>
523    <D:locktype><D:write/></D:locktype>
524    <D:owner>
525        <D:href>http://example.org/~ejw/contact.html</D:href>
526    </D:owner>
527</D:lockinfo>');
528
529        $this->server->httpRequest = $request;
530        $this->server->exec();
531
532        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
533        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
534
535        $this->assertEquals(200, $this->response->status);
536
537        $serverVars = [
538            'REQUEST_URI'    => '/dir/child.txt',
539            'REQUEST_METHOD' => 'DELETE',
540            'HTTP_IF'        => '(' . $this->response->getHeader('Lock-Token') . ')',
541        ];
542
543        $request = HTTP\Sapi::createFromServerArray($serverVars);
544        $this->server->httpRequest = $request;
545        $this->server->exec();
546
547        $this->assertEquals(204, $this->response->status);
548        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
549
550    }
551
552    /**
553     * @depends testLock
554     */
555    function testLockCopyLockSource() {
556
557        $serverVars = [
558            'REQUEST_URI'    => '/dir/child.txt',
559            'REQUEST_METHOD' => 'LOCK',
560        ];
561
562        $request = HTTP\Sapi::createFromServerArray($serverVars);
563        $request->setBody('<?xml version="1.0"?>
564<D:lockinfo xmlns:D="DAV:">
565    <D:lockscope><D:exclusive/></D:lockscope>
566    <D:locktype><D:write/></D:locktype>
567    <D:owner>
568        <D:href>http://example.org/~ejw/contact.html</D:href>
569    </D:owner>
570</D:lockinfo>');
571
572        $this->server->httpRequest = $request;
573        $this->server->exec();
574
575        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
576        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
577
578        $this->assertEquals(200, $this->response->status);
579
580        $serverVars = [
581            'REQUEST_URI'      => '/dir/child.txt',
582            'REQUEST_METHOD'   => 'COPY',
583            'HTTP_DESTINATION' => '/dir/child2.txt',
584        ];
585
586        $request = HTTP\Sapi::createFromServerArray($serverVars);
587        $this->server->httpRequest = $request;
588        $this->server->exec();
589
590        $this->assertEquals(201, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination');
591        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
592
593    }
594    /**
595     * @depends testLock
596     */
597    function testLockCopyLockDestination() {
598
599        $serverVars = [
600            'REQUEST_URI'    => '/dir/child2.txt',
601            'REQUEST_METHOD' => 'LOCK',
602        ];
603
604        $request = HTTP\Sapi::createFromServerArray($serverVars);
605        $request->setBody('<?xml version="1.0"?>
606<D:lockinfo xmlns:D="DAV:">
607    <D:lockscope><D:exclusive/></D:lockscope>
608    <D:locktype><D:write/></D:locktype>
609    <D:owner>
610        <D:href>http://example.org/~ejw/contact.html</D:href>
611    </D:owner>
612</D:lockinfo>');
613
614        $this->server->httpRequest = $request;
615        $this->server->exec();
616
617        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
618        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
619
620        $this->assertEquals(201, $this->response->status);
621
622        $serverVars = [
623            'REQUEST_URI'      => '/dir/child.txt',
624            'REQUEST_METHOD'   => 'COPY',
625            'HTTP_DESTINATION' => '/dir/child2.txt',
626        ];
627
628        $request = HTTP\Sapi::createFromServerArray($serverVars);
629        $this->server->httpRequest = $request;
630        $this->server->exec();
631
632        $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination');
633        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
634
635    }
636
637    /**
638     * @depends testLock
639     */
640    function testLockMoveLockSourceLocked() {
641
642        $serverVars = [
643            'REQUEST_URI'    => '/dir/child.txt',
644            'REQUEST_METHOD' => 'LOCK',
645        ];
646
647        $request = HTTP\Sapi::createFromServerArray($serverVars);
648        $request->setBody('<?xml version="1.0"?>
649<D:lockinfo xmlns:D="DAV:">
650    <D:lockscope><D:exclusive/></D:lockscope>
651    <D:locktype><D:write/></D:locktype>
652    <D:owner>
653        <D:href>http://example.org/~ejw/contact.html</D:href>
654    </D:owner>
655</D:lockinfo>');
656
657        $this->server->httpRequest = $request;
658        $this->server->exec();
659
660        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
661        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
662
663        $this->assertEquals(200, $this->response->status);
664
665        $serverVars = [
666            'REQUEST_URI'      => '/dir/child.txt',
667            'REQUEST_METHOD'   => 'MOVE',
668            'HTTP_DESTINATION' => '/dir/child2.txt',
669        ];
670
671        $request = HTTP\Sapi::createFromServerArray($serverVars);
672        $this->server->httpRequest = $request;
673        $this->server->exec();
674
675        $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination');
676        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
677
678    }
679
680    /**
681     * @depends testLock
682     */
683    function testLockMoveLockSourceSucceed() {
684
685        $serverVars = [
686            'REQUEST_URI'    => '/dir/child.txt',
687            'REQUEST_METHOD' => 'LOCK',
688        ];
689
690        $request = HTTP\Sapi::createFromServerArray($serverVars);
691        $request->setBody('<?xml version="1.0"?>
692<D:lockinfo xmlns:D="DAV:">
693    <D:lockscope><D:exclusive/></D:lockscope>
694    <D:locktype><D:write/></D:locktype>
695    <D:owner>
696        <D:href>http://example.org/~ejw/contact.html</D:href>
697    </D:owner>
698</D:lockinfo>');
699
700        $this->server->httpRequest = $request;
701        $this->server->exec();
702
703        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
704        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
705
706        $this->assertEquals(200, $this->response->status);
707
708        $serverVars = [
709            'REQUEST_URI'      => '/dir/child.txt',
710            'REQUEST_METHOD'   => 'MOVE',
711            'HTTP_DESTINATION' => '/dir/child2.txt',
712            'HTTP_IF'          => '(' . $this->response->getHeader('Lock-Token') . ')',
713        ];
714
715        $request = HTTP\Sapi::createFromServerArray($serverVars);
716        $this->server->httpRequest = $request;
717        $this->server->exec();
718
719        $this->assertEquals(201, $this->response->status, 'A valid lock-token was provided for the source, so this MOVE operation must succeed. Full response body: ' . $this->response->body);
720
721    }
722
723    /**
724     * @depends testLock
725     */
726    function testLockMoveLockDestination() {
727
728        $serverVars = [
729            'REQUEST_URI'    => '/dir/child2.txt',
730            'REQUEST_METHOD' => 'LOCK',
731        ];
732
733        $request = HTTP\Sapi::createFromServerArray($serverVars);
734        $request->setBody('<?xml version="1.0"?>
735<D:lockinfo xmlns:D="DAV:">
736    <D:lockscope><D:exclusive/></D:lockscope>
737    <D:locktype><D:write/></D:locktype>
738    <D:owner>
739        <D:href>http://example.org/~ejw/contact.html</D:href>
740    </D:owner>
741</D:lockinfo>');
742
743        $this->server->httpRequest = $request;
744        $this->server->exec();
745
746        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
747        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
748
749        $this->assertEquals(201, $this->response->status);
750
751        $serverVars = [
752            'REQUEST_URI'      => '/dir/child.txt',
753            'REQUEST_METHOD'   => 'MOVE',
754            'HTTP_DESTINATION' => '/dir/child2.txt',
755        ];
756
757        $request = HTTP\Sapi::createFromServerArray($serverVars);
758        $this->server->httpRequest = $request;
759        $this->server->exec();
760
761        $this->assertEquals(423, $this->response->status, 'Copy must succeed if only the source is locked, but not the destination');
762        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
763
764    }
765    /**
766     * @depends testLock
767     */
768    function testLockMoveLockParent() {
769
770        $serverVars = [
771            'REQUEST_URI'    => '/dir',
772            'REQUEST_METHOD' => 'LOCK',
773            'HTTP_DEPTH'     => 'infinite',
774        ];
775
776        $request = HTTP\Sapi::createFromServerArray($serverVars);
777        $request->setBody('<?xml version="1.0"?>
778<D:lockinfo xmlns:D="DAV:">
779    <D:lockscope><D:exclusive/></D:lockscope>
780    <D:locktype><D:write/></D:locktype>
781    <D:owner>
782        <D:href>http://example.org/~ejw/contact.html</D:href>
783    </D:owner>
784</D:lockinfo>');
785
786        $this->server->httpRequest = $request;
787        $this->server->exec();
788
789        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
790        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
791
792        $this->assertEquals(200, $this->response->status);
793
794        $serverVars = [
795            'REQUEST_URI'      => '/dir/child.txt',
796            'REQUEST_METHOD'   => 'MOVE',
797            'HTTP_DESTINATION' => '/dir/child2.txt',
798            'HTTP_IF'          => '</dir> (' . $this->response->getHeader('Lock-Token') . ')',
799        ];
800
801        $request = HTTP\Sapi::createFromServerArray($serverVars);
802        $this->server->httpRequest = $request;
803        $this->server->exec();
804
805        $this->assertEquals(201, $this->response->status, 'We locked the parent of both the source and destination, but the move didn\'t succeed.');
806        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
807
808    }
809
810    /**
811     * @depends testLock
812     */
813    function testLockPutGoodToken() {
814
815        $serverVars = [
816            'REQUEST_URI'    => '/test.txt',
817            'REQUEST_METHOD' => 'LOCK',
818        ];
819
820        $request = HTTP\Sapi::createFromServerArray($serverVars);
821        $request->setBody('<?xml version="1.0"?>
822<D:lockinfo xmlns:D="DAV:">
823    <D:lockscope><D:exclusive/></D:lockscope>
824    <D:locktype><D:write/></D:locktype>
825    <D:owner>
826        <D:href>http://example.org/~ejw/contact.html</D:href>
827    </D:owner>
828</D:lockinfo>');
829
830        $this->server->httpRequest = $request;
831        $this->server->exec();
832
833        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
834        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
835
836        $this->assertEquals(200, $this->response->status);
837
838        $serverVars = [
839            'REQUEST_URI'    => '/test.txt',
840            'REQUEST_METHOD' => 'PUT',
841            'HTTP_IF'        => '(' . $this->response->getHeader('Lock-Token') . ')',
842        ];
843
844        $request = HTTP\Sapi::createFromServerArray($serverVars);
845        $request->setBody('newbody');
846        $this->server->httpRequest = $request;
847        $this->server->exec();
848
849        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
850        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
851
852        $this->assertEquals(204, $this->response->status);
853
854    }
855
856    /**
857     * @depends testLock
858     */
859    function testLockPutUnrelatedToken() {
860
861        $request = new HTTP\Request('LOCK', '/unrelated.txt');
862        $request->setBody('<?xml version="1.0"?>
863<D:lockinfo xmlns:D="DAV:">
864    <D:lockscope><D:exclusive/></D:lockscope>
865    <D:locktype><D:write/></D:locktype>
866    <D:owner>
867        <D:href>http://example.org/~ejw/contact.html</D:href>
868    </D:owner>
869</D:lockinfo>');
870
871        $this->server->httpRequest = $request;
872        $this->server->exec();
873
874        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
875        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
876
877        $this->assertEquals(201, $this->response->getStatus());
878
879        $request = new HTTP\Request(
880            'PUT',
881            '/test.txt',
882            ['If' => '</unrelated.txt> (' . $this->response->getHeader('Lock-Token') . ')']
883        );
884        $request->setBody('newbody');
885        $this->server->httpRequest = $request;
886        $this->server->exec();
887
888        $this->assertEquals('application/xml; charset=utf-8', $this->response->getHeader('Content-Type'));
889        $this->assertTrue(preg_match('/^<opaquelocktoken:(.*)>$/', $this->response->getHeader('Lock-Token')) === 1, 'We did not get a valid Locktoken back (' . $this->response->getHeader('Lock-Token') . ')');
890
891        $this->assertEquals(204, $this->response->status);
892
893    }
894
895    function testPutWithIncorrectETag() {
896
897        $serverVars = [
898            'REQUEST_URI'    => '/test.txt',
899            'REQUEST_METHOD' => 'PUT',
900            'HTTP_IF'        => '(["etag1"])',
901        ];
902
903        $request = HTTP\Sapi::createFromServerArray($serverVars);
904        $request->setBody('newbody');
905        $this->server->httpRequest = $request;
906        $this->server->exec();
907        $this->assertEquals(412, $this->response->status);
908
909    }
910
911    /**
912     * @depends testPutWithIncorrectETag
913     */
914    function testPutWithCorrectETag() {
915
916        // We need an ETag-enabled file node.
917        $tree = new DAV\Tree(new DAV\FSExt\Directory(SABRE_TEMPDIR));
918        $this->server->tree = $tree;
919
920        $filename = SABRE_TEMPDIR . '/test.txt';
921        $etag = sha1(
922            fileinode($filename) .
923            filesize($filename) .
924            filemtime($filename)
925        );
926        $serverVars = [
927            'REQUEST_URI'    => '/test.txt',
928            'REQUEST_METHOD' => 'PUT',
929            'HTTP_IF'        => '(["' . $etag . '"])',
930        ];
931
932        $request = HTTP\Sapi::createFromServerArray($serverVars);
933        $request->setBody('newbody');
934        $this->server->httpRequest = $request;
935        $this->server->exec();
936        $this->assertEquals(204, $this->response->status, 'Incorrect status received. Full response body:' . $this->response->body);
937
938    }
939
940    function testDeleteWithETagOnCollection() {
941
942        $serverVars = [
943            'REQUEST_URI'    => '/dir',
944            'REQUEST_METHOD' => 'DELETE',
945            'HTTP_IF'        => '(["etag1"])',
946        ];
947        $request = HTTP\Sapi::createFromServerArray($serverVars);
948
949        $this->server->httpRequest = $request;
950        $this->server->exec();
951        $this->assertEquals(412, $this->response->status);
952
953    }
954
955    function testGetTimeoutHeader() {
956
957        $request = HTTP\Sapi::createFromServerArray([
958            'HTTP_TIMEOUT' => 'second-100',
959        ]);
960
961        $this->server->httpRequest = $request;
962        $this->assertEquals(100, $this->locksPlugin->getTimeoutHeader());
963
964    }
965
966    function testGetTimeoutHeaderTwoItems() {
967
968        $request = HTTP\Sapi::createFromServerArray([
969            'HTTP_TIMEOUT' => 'second-5, infinite',
970        ]);
971
972        $this->server->httpRequest = $request;
973        $this->assertEquals(5, $this->locksPlugin->getTimeoutHeader());
974
975    }
976
977    function testGetTimeoutHeaderInfinite() {
978
979        $request = HTTP\Sapi::createFromServerArray([
980            'HTTP_TIMEOUT' => 'infinite, second-5',
981        ]);
982
983        $this->server->httpRequest = $request;
984        $this->assertEquals(LockInfo::TIMEOUT_INFINITE, $this->locksPlugin->getTimeoutHeader());
985
986    }
987
988    /**
989     * @expectedException Sabre\DAV\Exception\BadRequest
990     */
991    function testGetTimeoutHeaderInvalid() {
992
993        $request = HTTP\Sapi::createFromServerArray([
994            'HTTP_TIMEOUT' => 'yourmom',
995        ]);
996
997        $this->server->httpRequest = $request;
998        $this->locksPlugin->getTimeoutHeader();
999
1000    }
1001
1002
1003}
1004