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