]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/lib/Sabre/DAV/PartialUpdate/Plugin.php
Update strings
[friendica-addons.git] / dav / SabreDAV / lib / Sabre / DAV / PartialUpdate / Plugin.php
1 <?php
2 /**
3  * Partial update addon (Patch method)
4  *
5  * This addon provides a way to modify only part of a target resource
6  * It may bu used to update a file chunk, upload big a file into smaller
7  * chunks or resume an upload.
8  *
9  * $patchPlugin = new Sabre_DAV_Patch_Plugin();
10  * $server->addPlugin($patchPlugin);
11  *
12  * @package Sabre
13  * @subpackage DAV
14  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
15  * @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
16  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
17  */
18 class Sabre_DAV_PartialUpdate_Plugin extends Sabre_DAV_ServerPlugin {
19
20     /**
21      * Reference to server
22      *
23      * @var Sabre_DAV_Server
24      */
25     protected $server;
26
27     /**
28      * Initializes the addon
29      *
30      * This method is automatically called by the Server class after addPlugin.
31      *
32      * @param Sabre_DAV_Server $server
33      * @return void
34      */
35     public function initialize(Sabre_DAV_Server $server) {
36
37         $this->server = $server;
38         $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
39
40     }
41
42     /**
43      * Returns a addon name.
44      *
45      * Using this name other addons will be able to access other addons
46      * using Sabre_DAV_Server::getPlugin
47      *
48      * @return string
49      */
50     public function getPluginName() {
51
52         return 'partialupdate';
53
54     }
55
56     /**
57      * This method is called by the Server if the user used an HTTP method
58      * the server didn't recognize.
59      *
60      * This addon intercepts the PATCH methods.
61      *
62      * @param string $method
63      * @param string $uri
64      * @return bool|null
65      */
66     public function unknownMethod($method, $uri) {
67
68         switch($method) {
69             
70             case 'PATCH':
71                 return $this->httpPatch($uri);
72
73         }
74
75     }
76
77     /**
78      * Use this method to tell the server this addon defines additional
79      * HTTP methods.
80      *
81      * This method is passed a uri. It should only return HTTP methods that are
82      * available for the specified uri.
83      * 
84      * We claim to support PATCH method (partial update) if and only if
85      *     - the node exist
86      *     - the node implements our partial update interface
87      *
88      * @param string $uri
89      * @return array
90      */
91     public function getHTTPMethods($uri) {
92         
93         $tree = $this->server->tree;
94         
95         if ($tree->nodeExists($uri) && 
96             $tree->getNodeForPath($uri) instanceof Sabre_DAV_PartialUpdate_IFile) {
97             return array('PATCH');
98          }
99          
100          return array();
101
102     }
103
104     /**
105      * Returns a list of features for the HTTP OPTIONS Dav: header.
106      *
107      * @return array
108      */
109     public function getFeatures() {
110
111         return array('sabredav-partialupdate');
112
113     }
114
115     /**
116      * Patch an uri
117      *
118      * The WebDAV patch request can be used to modify only a part of an 
119      * existing resource. If the resource does not exist yet and the first
120      * offset is not 0, the request fails
121      *
122      * @param string $uri
123      * @return void
124      */
125     protected function httpPatch($uri) {
126
127         // Get the node. Will throw a 404 if not found
128         $node = $this->server->tree->getNodeForPath($uri);
129         if (!($node instanceof Sabre_DAV_PartialUpdate_IFile)) {
130             throw new Sabre_DAV_Exception_MethodNotAllowed('The target resource does not support the PATCH method.');
131         }
132
133         $range = $this->getHTTPUpdateRange();
134
135         if (!$range) {
136             throw new Sabre_DAV_Exception_BadRequest('No valid "X-Update-Range" found in the headers');
137         }
138         
139         $contentType = strtolower(
140             $this->server->httpRequest->getHeader('Content-Type')
141         );
142         
143         if ($contentType != 'application/x-sabredav-partialupdate') {
144             throw new Sabre_DAV_Exception_UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"');
145         }
146
147         $len = $this->server->httpRequest->getHeader('Content-Length');
148
149         // Load the begin and end data
150         $start = ($range[0])?$range[0]:0;
151         $end   = ($range[1])?$range[1]:$len-1;
152
153         // Check consistency
154         if($end < $start)
155             throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
156         if($end - $start + 1 != $len)
157             throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets');
158
159         // Checking If-None-Match and related headers.
160         if (!$this->server->checkPreconditions()) return;
161
162         if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null)))
163             return;
164
165         $body = $this->server->httpRequest->getBody();
166         $etag = $node->putRange($body, $start-1);
167
168         $this->server->broadcastEvent('afterWriteContent',array($uri, $node));
169
170         $this->server->httpResponse->setHeader('Content-Length','0');
171         if ($etag) $this->server->httpResponse->setHeader('ETag',$etag);
172         $this->server->httpResponse->sendStatus(204);
173
174         return false;
175
176     }
177     
178    /**
179      * Returns the HTTP custom range update header
180      *
181      * This method returns null if there is no well-formed HTTP range request
182      * header or array($start, $end).
183      *
184      * The first number is the offset of the first byte in the range.
185      * The second number is the offset of the last byte in the range.
186      *
187      * If the second offset is null, it should be treated as the offset of the last byte of the entity
188      * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
189      *
190      * @return array|null
191      */
192     public function getHTTPUpdateRange() {
193
194         $range = $this->server->httpRequest->getHeader('X-Update-Range');
195         if (is_null($range)) return null;
196
197         // Matching "Range: bytes=1234-5678: both numbers are optional
198
199         if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
200
201         if ($matches[1]==='' && $matches[2]==='') return null;
202
203         return array(
204             $matches[1]!==''?$matches[1]:null,
205             $matches[2]!==''?$matches[2]:null,
206         );
207
208     }
209 }