Skip to content

Commit bcd7b8f

Browse files
committed
Fixes Issue 25.
1 parent b797c4b commit bcd7b8f

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Added: MySQL database dumps. MySQL is now also supported next to SQLite.
55
* Added: expand-properties REPORT from RFC 3253.
66
* Added: Sabre_DAV_Property_IHref interface for properties exposing urls.
7+
* Added: Issue 25: Throwing error on broken Finder behaviour.
78
* Changed: Authentication backend is now aware of current user.
89

910
1.2.0alpha1 (2010-03-31)

lib/Sabre/DAV/Server.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,50 @@ protected function httpPropPatch() {
527527
*/
528528
protected function httpPut() {
529529

530+
$body = $this->httpRequest->getBody();
531+
532+
// Intercepting the Finder problem
533+
if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
534+
535+
/**
536+
Many webservers will not cooperate well with Finder PUT requests,
537+
because it uses 'Chunked' transfer encoding for the request body.
538+
539+
The symptom of this problem is that Finder sends files to the
540+
server, but they arrive as 0-lenght files in PHP.
541+
542+
If we don't do anything, the user might think they are uploading
543+
files successfully, but they end up empty on the server. Instead,
544+
we throw back an error if we detect this.
545+
546+
The reason Finder uses Chunked, is because it thinks the files
547+
might change as it's being uploaded, and therefore the
548+
Content-Length can vary.
549+
550+
Instead it sends the X-Expected-Entity-Length header with the size
551+
of the file at the very start of the request. If this header is set,
552+
but we don't get a request body we will fail the request to
553+
protect the end-user.
554+
*/
555+
556+
// Only reading first byte
557+
$firstByte = fread($body,1);
558+
if (strlen($firstByte)!==1) {
559+
throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
560+
}
561+
562+
// The body needs to stay intact, so we copy everything to a
563+
// temporary stream.
564+
565+
$newBody = fopen('php://temp','r+');
566+
fwrite($newBody,$firstByte);
567+
stream_copy_to_stream($body, $newBody);
568+
rewind($newBody);
569+
570+
$body = $newBody;
571+
572+
}
573+
530574
// First we'll do a check to see if the resource already exists
531575
try {
532576

@@ -544,14 +588,14 @@ protected function httpPut() {
544588
if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.');
545589
if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false;
546590

547-
$node->put($this->httpRequest->getBody());
591+
$node->put($body);
548592
$this->httpResponse->setHeader('Content-Length','0');
549593
$this->httpResponse->sendStatus(200);
550594

551595
} catch (Sabre_DAV_Exception_FileNotFound $e) {
552596

553597
// If we got here, the resource didn't exist yet.
554-
$this->createFile($this->getRequestUri(),$this->httpRequest->getBody());
598+
$this->createFile($this->getRequestUri(),$body);
555599
$this->httpResponse->setHeader('Content-Length','0');
556600
$this->httpResponse->sendStatus(201);
557601

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
require_once 'Sabre/HTTP/ResponseMock.php';
4+
require_once 'Sabre/DAV/AbstractServer.php';
5+
require_once 'Sabre/DAV/Exception.php';
6+
7+
class Sabre_DAV_ServerFinderBlockTest extends Sabre_DAV_AbstractServer{
8+
9+
function testPut() {
10+
11+
$serverVars = array(
12+
'REQUEST_URI' => '/testput.txt',
13+
'REQUEST_METHOD' => 'PUT',
14+
'HTTP_X_EXPECTED_ENTITY_LENGTH' => '20',
15+
);
16+
17+
$request = new Sabre_HTTP_Request($serverVars);
18+
$request->setBody('Testing finder');
19+
$this->server->httpRequest = $request;
20+
$this->server->exec();
21+
22+
$this->assertEquals('', $this->response->body);
23+
$this->assertEquals('HTTP/1.1 201 Created',$this->response->status);
24+
$this->assertEquals(array(
25+
'Content-Length' => '0',
26+
),$this->response->headers);
27+
28+
$this->assertEquals('Testing finder',file_get_contents(SABRE_TEMPDIR . '/testput.txt'));
29+
30+
}
31+
32+
function testPutFail() {
33+
34+
$serverVars = array(
35+
'REQUEST_URI' => '/testput.txt',
36+
'REQUEST_METHOD' => 'PUT',
37+
'HTTP_X_EXPECTED_ENTITY_LENGTH' => '20',
38+
);
39+
40+
$request = new Sabre_HTTP_Request($serverVars);
41+
$request->setBody('');
42+
$this->server->httpRequest = $request;
43+
$this->server->exec();
44+
45+
$this->assertEquals('HTTP/1.1 403 Forbidden',$this->response->status);
46+
$this->assertEquals(array(
47+
'Content-Type' => 'application/xml; charset=utf-8',
48+
),$this->response->headers);
49+
50+
$this->assertFalse(file_exists(SABRE_TEMPDIR . '/testput.txt'));
51+
}
52+
}
53+
54+
?>

0 commit comments

Comments
 (0)