ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
Server.php
Go to the documentation of this file.
1<?php
2//
3// +----------------------------------------------------------------------+
4// | PHP Version 4 |
5// +----------------------------------------------------------------------+
6// | Copyright (c) 1997-2003 The PHP Group |
7// +----------------------------------------------------------------------+
8// | This source file is subject to version 2.02 of the PHP license, |
9// | that is bundled with this package in the file LICENSE, and is |
10// | available at through the world-wide-web at |
11// | http://www.php.net/license/2_02.txt. |
12// | If you did not receive a copy of the PHP license and are unable to |
13// | obtain it through the world-wide-web, please send a note to |
14// | license@php.net so we can mail you a copy immediately. |
15// +----------------------------------------------------------------------+
16// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
17// | Christian Stocker <chregu@bitflux.ch> |
18// +----------------------------------------------------------------------+
19//
20// $Id: Server.php,v 1.28 2005/04/05 22:51:09 hholzgra Exp $
21//
22
23require_once "Services/WebDAV/classes/Tools/_parse_propfind.php";
24require_once "Services/WebDAV/classes/Tools/_parse_proppatch.php";
25require_once "Services/WebDAV/classes/Tools/_parse_lockinfo.php";
26
27
38{
39 // {{{ Member Variables
40
46 public $uri;
47
48
54 public $base_uri;
55
56
62 public $path;
63
69 public $http_auth_realm = "PHP WebDAV";
70
76 public $dav_powered_by = "";
77
83 public $_if_header_uris = array();
84
90 public $_http_status = "200 OK";
91
97 public $_prop_encoding = "utf-8";
98
99 // }}}
100
101 // {{{ Constructor
102
108 private function __construct()
109 {
110 // PHP messages destroy XML output -> switch them off
111 //ini_set("display_errors", 0);
112 }
113
114 // }}}
115
116 // {{{ ServeRequest()
125 public function serveRequest()
126 {
127 // default uri is the complete request uri
128 // FIXME: use ilHTTPS::isDetected
129 $uri = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:");
130 $uri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
131
132 $this->base_uri = $uri;
133 $this->uri = $uri . $_SERVER[PATH_INFO];
134
135 // identify ourselves
136 if (empty($this->dav_powered_by)) {
137 header("X-Dav-Powered-By: PHP class: " . get_class($this));
138 } else {
139 header("X-Dav-Powered-By: " . $this->dav_powered_by);
140 }
141
142 $this->writelog(__METHOD__ . ': Using uri: ' . $this->uri);
143
144 // check authentication
145 if (!$this->_check_auth()) {
146 // RFC2518 says we must use Digest instead of Basic
147 // but Microsoft Clients do not support Digest
148 // and we don't support NTLM and Kerberos
149 // so we are stuck with Basic here
150 header('WWW-Authenticate: Basic realm="' . ($this->http_auth_realm) . '"');
151
152 // Windows seems to require this being the last header sent
153 // (changed according to PECL bug #3138)
154 $this->http_status('401 Unauthorized');
155 $this->writelog('Check auth failed');
156
157 return;
158 }
159
160 // check
161 if (!$this->_check_if_header_conditions()) {
162 $this->writelog(__METHOD__ . ': Precondition failed.');
163 $this->http_status("412 Precondition failed");
164 return;
165 }
166
167 // set path
168 $this->path = $this->_urldecode($_SERVER["PATH_INFO"]);
169 if (!strlen($this->path)) {
170 header("Location: " . $this->base_uri . "/");
171 $this->writelog('HTTP_WebDAV_Server.ServeRequest() missing path info');
172 $this->path = '/';
173 //exit;
174 }
175 // BEGIN WebDAV: Don't strip backslashes. Backslashes are a valid part of a unix filename!
176 /*
177 if(ini_get("magic_quotes_gpc")) {
178 $this->path = stripslashes($this->path);
179 }*/
180 // END PATCH WebDAV: Don't strip backslashes. Backslashes are a valid part of a unix filename!
181
182
183 // detect requested method names
184 $method = strtolower($_SERVER["REQUEST_METHOD"]);
185 $wrapper = "http_" . $method;
186
187 $this->writelog(__METHOD__ . ': Using request method: ' . $method);
188
189 // activate HEAD emulation by GET if no HEAD method found
190 if ($method == "head" && !method_exists($this, "head")) {
191 $method = "get";
192 $this->writelog(__METHOD__ . ': Using head emulation by get.');
193 }
194
195 if (method_exists($this, $wrapper) && ($method == "options" || method_exists($this, $method))) {
196 $this->writelog(__METHOD__ . ': Calling wrapper: ' . $wrapper);
197 $this->$wrapper(); // call method by name
198 } else { // method not found/implemented
199 if ($_SERVER["REQUEST_METHOD"] == "LOCK") {
200 $this->writelog(__METHOD__ . ': Method not found/implemented. Sending 412');
201 $this->http_status("412 Precondition failed");
202 } else {
203 $this->writelog(__METHOD__ . ': Method not found/implemented. Sending allowd methods');
204 $this->http_status("405 Method not allowed");
205 header("Allow: " . join(", ", $this->_allow())); // tell client what's allowed
206 }
207 }
208 }
209
210 // }}}
211
212 // {{{ abstract WebDAV methods
213
214 // {{{ GET()
233 /* abstract
234 function GET(&$params)
235 {
236 // dummy entry for PHPDoc
237 }
238 */
239
240 // }}}
241
242 // {{{ PUT()
253 /* abstract
254 function PUT()
255 {
256 // dummy entry for PHPDoc
257 }
258 */
259
260 // }}}
261
262 // {{{ COPY()
263
274 /* abstract
275 function COPY()
276 {
277 // dummy entry for PHPDoc
278 }
279 */
280
281 // }}}
282
283 // {{{ MOVE()
284
295 /* abstract
296 function MOVE()
297 {
298 // dummy entry for PHPDoc
299 }
300 */
301
302 // }}}
303
304 // {{{ DELETE()
305
316 /* abstract
317 function DELETE()
318 {
319 // dummy entry for PHPDoc
320 }
321 */
322 // }}}
323
324 // {{{ PROPFIND()
325
336 /* abstract
337 function PROPFIND()
338 {
339 // dummy entry for PHPDoc
340 }
341 */
342
343 // }}}
344
345 // {{{ PROPPATCH()
346
357 /* abstract
358 function PROPPATCH()
359 {
360 // dummy entry for PHPDoc
361 }
362 */
363 // }}}
364
365 // {{{ LOCK()
366
377 /* abstract
378 function LOCK()
379 {
380 // dummy entry for PHPDoc
381 }
382 */
383 // }}}
384
385 // {{{ UNLOCK()
386
397 /* abstract
398 function UNLOCK()
399 {
400 // dummy entry for PHPDoc
401 }
402 */
403 // }}}
404
405 // }}}
406
407 // {{{ other abstract methods
408
409 // {{{ check_auth()
410
423 /* abstract
424 function checkAuth($type, $username, $password)
425 {
426 // dummy entry for PHPDoc
427 }
428 */
429
430 // }}}
431
432 // {{{ checklock()
433
446 /* abstract
447 function checklock($resource)
448 {
449 // dummy entry for PHPDoc
450 }
451 */
452
453 // }}}
454
455 // }}}
456
457 // {{{ WebDAV HTTP method wrappers
458
459 // {{{ http_OPTIONS()
460
471 public function http_OPTIONS()
472 {
473 // Microsoft clients default to the Frontpage protocol
474 // unless we tell them to use WebDAV
475 header("MS-Author-Via: DAV");
476
477 // get allowed methods
478 $allow = $this->_allow();
479
480 // dav header
481 $dav = array(1); // assume we are always dav class 1 compliant
482 if (isset($allow['LOCK'])) {
483 $dav[] = 2; // dav class 2 requires that locking is supported
484 }
485
486 // tell clients what we found
487 $this->http_status("200 OK");
488 header("DAV: " . join(",", $dav));
489 header("Allow: " . join(", ", $allow));
490 $this->writelog(__METHOD__ . ': dav=' . var_export($dav, true) . ' allow=' . var_export($allow, true));
491 header("Content-length: 0");
492 }
493
494 // }}}
495
496
497 // {{{ http_PROPFIND()
498
505 public function http_PROPFIND()
506 {
507 $options = array();
508 $options["path"] = $this->path;
509
510 // search depth from header (default is "infinity)
511 if (isset($_SERVER['HTTP_DEPTH'])) {
512 $options["depth"] = $_SERVER["HTTP_DEPTH"];
513 } else {
514 $options["depth"] = "infinity";
515 }
516
517 // analyze request payload
518 $propinfo = new _parse_propfind("php://input");
519 if (!$propinfo->success) {
520 $this->http_status("400 Error");
521 return;
522 }
523 $options['props'] = $propinfo->props;
524
525 // call user handler
526 $files = array();
527 if (!$this->propfind($options, $files)) {
528 $this->http_status("404 Not Found");
529 return;
530 }
531
532 // collect namespaces here
533 $ns_hash = array();
534
535 // Microsoft Clients need this special namespace for date and time values
536 $ns_defs = "xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
537
538 // now we loop over all returned file entries
539 foreach ($files["files"] as $filekey => $file) {
540
541 // nothing to do if no properties were returend for a file
542 if (!isset($file["props"]) || !is_array($file["props"])) {
543 continue;
544 }
545
546 // now loop over all returned properties
547 foreach ($file["props"] as $key => $prop) {
548 // as a convenience feature we do not require that user handlers
549 // restrict returned properties to the requested ones
550 // here we strip all unrequested entries out of the response
551
552 switch ($options['props']) {
553 case "all":
554 // nothing to remove
555 break;
556
557 case "names":
558 // only the names of all existing properties were requested
559 // so we remove all values
560 unset($files["files"][$filekey]["props"][$key]["val"]);
561 break;
562
563 default:
564 $found = false;
565
566 // search property name in requested properties
567 foreach ((array) $options["props"] as $reqprop) {
568 if ($reqprop["name"] == $prop["name"]
569 && $reqprop["xmlns"] == $prop["ns"]) {
570 $found = true;
571 break;
572 }
573 }
574
575 // unset property and continue with next one if not found/requested
576 if (!$found) {
577 $files["files"][$filekey]["props"][$key]="";
578 continue(2);
579 }
580 break;
581 }
582
583 // namespace handling
584 if (empty($prop["ns"])) {
585 continue;
586 } // no namespace
587 $ns = $prop["ns"];
588 if ($ns == "DAV:") {
589 continue;
590 } // default namespace
591 if (isset($ns_hash[$ns])) {
592 continue;
593 } // already known
594
595 // register namespace
596 $ns_name = "ns" . (count($ns_hash) + 1);
597 $ns_hash[$ns] = $ns_name;
598 $ns_defs .= " xmlns:$ns_name=\"$ns\"";
599 }
600
601 // we also need to add empty entries for properties that were requested
602 // but for which no values where returned by the user handler
603 if (is_array($options['props'])) {
604 foreach ($options["props"] as $reqprop) {
605 if ($reqprop['name']=="") {
606 continue;
607 } // skip empty entries
608
609 $found = false;
610
611 // check if property exists in result
612 foreach ($file["props"] as $prop) {
613 if ($reqprop["name"] == $prop["name"]
614 && $reqprop["xmlns"] == $prop["ns"]) {
615 $found = true;
616 break;
617 }
618 }
619
620 if (!$found) {
621 if ($reqprop["xmlns"]==="DAV:" && $reqprop["name"]==="lockdiscovery") {
622 // lockdiscovery is handled by the base class
623 $files["files"][$filekey]["props"][]
624 = $this->mkprop(
625 "DAV:",
626 "lockdiscovery",
627 $this->lockdiscovery($files["files"][$filekey]['path'])
628 );
629 } else {
630 // add empty value for this property
631 $files["files"][$filekey]["noprops"][] =
632 $this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
633
634 // register property namespace if not known yet
635 if ($reqprop["xmlns"] != "DAV:" && !isset($ns_hash[$reqprop["xmlns"]])) {
636 $ns_name = "ns" . (count($ns_hash) + 1);
637 $ns_hash[$reqprop["xmlns"]] = $ns_name;
638 $ns_defs .= " xmlns:$ns_name=\"$reqprop[xmlns]\"";
639 }
640 }
641 }
642 }
643 }
644 }
645
646 // now we generate the reply header ...
647 $this->http_status("207 Multi-Status");
648 header('Content-Type: text/xml; charset="utf-8"');
649
650 // ... and payload
651 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
652 echo "<D:multistatus xmlns:D=\"DAV:\">\n";
653
654 foreach ($files["files"] as $file) {
655 // ignore empty or incomplete entries
656 if (!is_array($file) || empty($file) || !isset($file["path"])) {
657 continue;
658 }
659 $path = $file['path'];
660 if (!is_string($path) || $path==="") {
661 continue;
662 }
663
664 echo " <D:response $ns_defs>\n";
665
666 // BEGIN WebDAV W. Randelshofer Don't slashify path because it confuses Mac OS X
667 //$href = $this->_slashify($_SERVER['SCRIPT_NAME'] . $path);
668 $href = $_SERVER['SCRIPT_NAME'] . $path;
669 //END PATCH WebDAV W. Randelshofer
670
671 echo " <D:href>$href</D:href>\n";
672
673 // report all found properties and their values (if any)
674 if (isset($file["props"]) && is_array($file["props"])) {
675 echo " <D:propstat>\n";
676 echo " <D:prop>\n";
677
678 foreach ($file["props"] as $key => $prop) {
679 if (!is_array($prop)) {
680 continue;
681 }
682 if (!isset($prop["name"])) {
683 continue;
684 }
685 if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) {
686 // empty properties (cannot use empty() for check as "0" is a legal value here)
687 if ($prop["ns"]=="DAV:") {
688 echo " <D:$prop[name]/>\n";
689 } elseif (!empty($prop["ns"])) {
690 echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n";
691 } else {
692 echo " <$prop[name] xmlns=\"\"/>";
693 }
694 } elseif ($prop["ns"] == "DAV:") {
695 // some WebDAV properties need special treatment
696 switch ($prop["name"]) {
697 case "creationdate":
698 echo " <D:creationdate ns0:dt=\"dateTime.tz\">"
699 // BEGIN WebDAV W. Randelshofer
700 . gmdate("Y-m-d\\TH:i:s\\Z", $prop['val'])
701 // . gmdate("D, d M Y H:i:s ", $prop['val'])
702 // END PATCH WebDAV W. Randelshofer
703 . "</D:creationdate>\n";
704 break;
705 case "getlastmodified":
706 echo " <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"
707 . gmdate("D, d M Y H:i:s ", $prop['val'])
708 . "GMT</D:getlastmodified>\n";
709 break;
710 case "resourcetype":
711 echo " <D:resourcetype><D:$prop[val]/></D:resourcetype>\n";
712 break;
713 case "supportedlock":
714 echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
715 break;
716 case "lockdiscovery":
717 echo " <D:lockdiscovery>\n";
718 echo $prop["val"];
719 echo " </D:lockdiscovery>\n";
720 break;
721 default:
722 echo " <D:$prop[name]>"
723 . $this->_prop_encode(htmlspecialchars($prop['val']))
724 . "</D:$prop[name]>\n";
725 break;
726 }
727 } else {
728 // properties from namespaces != "DAV:" or without any namespace
729 if ($prop["ns"]) {
730 echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
731 . $this->_prop_encode(htmlspecialchars($prop['val']))
732 . "</" . $ns_hash[$prop["ns"]] . ":$prop[name]>\n";
733 } else {
734 echo " <$prop[name] xmlns=\"\">"
735 . $this->_prop_encode(htmlspecialchars($prop['val']))
736 . "</$prop[name]>\n";
737 }
738 }
739 }
740
741 echo " </D:prop>\n";
742 echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
743 echo " </D:propstat>\n";
744 }
745
746 // now report all properties requested but not found
747 if (isset($file["noprops"])) {
748 echo " <D:propstat>\n";
749 echo " <D:prop>\n";
750
751 foreach ($file["noprops"] as $key => $prop) {
752 if ($prop["ns"] == "DAV:") {
753 echo " <D:$prop[name]/>\n";
754 } elseif ($prop["ns"] == "") {
755 echo " <$prop[name] xmlns=\"\"/>\n";
756 } else {
757 echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n";
758 }
759 }
760
761 echo " </D:prop>\n";
762 echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
763 echo " </D:propstat>\n";
764 }
765
766 echo " </D:response>\n";
767 }
768
769 echo "</D:multistatus>\n";
770 }
771
772
773 // }}}
774
775 // {{{ http_PROPPATCH()
776
783 public function http_PROPPATCH()
784 {
785 if ($this->_check_lock_status($this->path)) {
786 $options = array();
787 $options["path"] = $this->path;
788
789 $propinfo = new _parse_proppatch("php://input");
790
791 if (!$propinfo->success) {
792 $this->http_status("400 Error");
793 return;
794 }
795
796 $options['props'] = $propinfo->props;
797
798 $responsedescr = $this->proppatch($options);
799
800 $this->http_status("207 Multi-Status");
801 header('Content-Type: text/xml; charset="utf-8"');
802
803 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
804
805 echo "<D:multistatus xmlns:D=\"DAV:\">\n";
806 echo " <D:response>\n";
807 echo " <D:href>" . $this->_urlencode($_SERVER["SCRIPT_NAME"] . $this->path) . "</D:href>\n";
808
809 foreach ($options["props"] as $prop) {
810 echo " <D:propstat>\n";
811 echo " <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
812 echo " <D:status>HTTP/1.1 $prop[status]</D:status>\n";
813 echo " </D:propstat>\n";
814 }
815
816 if ($responsedescr) {
817 echo " <D:responsedescription>" .
818 $this->_prop_encode(htmlspecialchars($responsedescr)) .
819 "</D:responsedescription>\n";
820 }
821
822 echo " </D:response>\n";
823 echo "</D:multistatus>\n";
824 } else {
825 $this->http_status("423 Locked");
826 }
827 }
828
829 // }}}
830
831
832 // {{{ http_MKCOL()
833
840 public function http_MKCOL()
841 {
842 $options = array();
843 $options["path"] = $this->path;
844
845 $stat = $this->mkcol($options);
846
847 $this->http_status($stat);
848 }
849
850 // }}}
851
852
853 // {{{ http_GET()
854
861 public function http_GET()
862 {
863 // TODO check for invalid stream
864 $options = array();
865 $options["path"] = $this->path;
866
867 $this->_get_ranges($options);
868
869 if (true === ($status = $this->get($options))) {
870 if (!headers_sent()) {
871 $status = "200 OK";
872
873 if (!isset($options['mimetype'])) {
874 $options['mimetype'] = "application/octet-stream";
875 }
876 header("Content-type: $options[mimetype]");
877
878 if (isset($options['mtime'])) {
879 header("Last-modified:" . gmdate("D, d M Y H:i:s ", $options['mtime']) . "GMT");
880 }
881
882 if (isset($options['stream'])) {
883 // GET handler returned a stream
884 if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) {
885 // partial request and stream is seekable
886
887 if (count($options['ranges']) === 1) {
888 $range = $options['ranges'][0];
889
890 if (isset($range['start'])) {
891 fseek($options['stream'], $range['start'], SEEK_SET);
892 if (feof($options['stream'])) {
893 $this->http_status("416 Requested range not satisfiable");
894 exit;
895 }
896
897 if (isset($range['end']) && $range['end'] != '') {
898 $size = $range['end']-$range['start']+1;
899 $this->http_status("206 partial");
900 header("Content-length: $size");
901 header("Content-range: $range[start]-$range[end]/"
902 . (isset($options['size']) ? $options['size'] : "*"));
903 while ($size && !feof($options['stream'])) {
904 $buffer = fread($options['stream'], 4096);
905 $size -= strlen($buffer);
906 echo $buffer;
907 }
908 } else {
909 $this->http_status("206 partial");
910 if (isset($options['size'])) {
911 header("Content-length: " . ($options['size'] - $range['start']));
912 header("Content-range: $start-$end/"
913 . (isset($options['size']) ? $options['size'] : "*"));
914 }
915 fpassthru($options['stream']);
916 }
917 } else {
918 header("Content-length: " . $range['last']);
919 fseek($options['stream'], -$range['last'], SEEK_END);
920 fpassthru($options['stream']);
921 }
922 } else {
923 $this->_multipart_byterange_header(); // init multipart
924 foreach ($options['ranges'] as $range) {
925 // TODO what if size unknown? 500?
926 if (isset($range['start'])) {
927 $from = $range['start'];
928 $to = !empty($range['end']) ? $range['end'] : $options['size']-1;
929 } else {
930 $from = $options['size'] - $range['last']-1;
931 $to = $options['size'] -1;
932 }
933 $total = isset($options['size']) ? $options['size'] : "*";
934 $size = $to - $from + 1;
935 $this->_multipart_byterange_header($options['mimetype'], $from, $to, $total);
936
937
938 fseek($options['stream'], $start, SEEK_SET);
939 while ($size && !feof($options['stream'])) {
940 $buffer = fread($options['stream'], 4096);
941 $size -= strlen($buffer);
942 echo $buffer;
943 }
944 }
945 $this->_multipart_byterange_header(); // end multipart
946 }
947 } else {
948 // normal request or stream isn't seekable, return full content
949 if (isset($options['size'])) {
950 header("Content-length: " . $options['size']);
951 }
952
953 // BEGIN WebDAV W. Randelshofer
954 // fpassthru apparently only delivers up to 2 million bytes.
955 // use fread instead
956 //fpassthru($options['stream']);
957 while (!feof($options['stream'])) {
958 $buffer = fread($options['stream'], 4096);
959 echo $buffer;
960 }
961 // END PATCH WebDAV W. Randelshofer
962
963 return; // no more headers
964 }
965 } elseif (isset($options['data'])) {
966 if (is_array($options['data'])) {
967 // reply to partial request
968 } else {
969 header("Content-length: " . strlen($options['data']));
970 echo $options['data'];
971 }
972 }
973 }
974 }
975
976 if (false === $status) {
977 // BEGIN WebDAV Randelshofer
978 $status = '404 Not Found';
979 //$this->http_status("404 not found");
980 // END PATCH WebDAV Randelshofer
981 }
982
983 if (!headers_sent()) {
984 // TODO: check setting of headers in various code pathes above
985 $this->http_status("$status");
986 }
987 }
988
989
996 public function _get_ranges(&$options)
997 {
998 // process Range: header if present
999 if (isset($_SERVER['HTTP_RANGE'])) {
1000
1001 // we only support standard "bytes" range specifications for now
1002 if (preg_match("/bytes[[:space:]]*=[[:space:]]*(.+)/", $_SERVER['HTTP_RANGE'], $matches)) {
1003 $options["ranges"] = array();
1004
1005 // ranges are comma separated
1006 foreach (explode(",", $matches[1]) as $range) {
1007 // ranges are either from-to pairs or just end positions
1008 list($start, $end) = explode("-", $range);
1009 $options["ranges"][] = ($start==="")
1010 ? array("last"=>$end)
1011 : array("start"=>$start, "end"=>$end);
1012 }
1013 }
1014 }
1015 }
1016
1030 public function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false)
1031 {
1032 if ($mimetype === false) {
1033 if (!isset($this->multipart_separator)) {
1034 // initial
1035
1036 // a little naive, this sequence *might* be part of the content
1037 // but it's really not likely and rather expensive to check
1038 $this->multipart_separator = "SEPARATOR_" . md5(microtime());
1039
1040 // generate HTTP header
1041 header("Content-type: multipart/byteranges; boundary=" . $this->multipart_separator);
1042 } else {
1043 // final
1044
1045 // generate closing multipart sequence
1046 echo "\n--{$this->multipart_separator}--";
1047 }
1048 } else {
1049 // generate separator and header for next part
1050 echo "\n--{$this->multipart_separator}\n";
1051 echo "Content-type: $mimetype\n";
1052 echo "Content-range: $from-$to/" . ($total === false ? "*" : $total);
1053 echo "\n\n";
1054 }
1055 }
1056
1057
1058
1059 // }}}
1060
1061 // {{{ http_HEAD()
1062
1069 public function http_HEAD()
1070 {
1071 $status = false;
1072 $options = array();
1073 $options["path"] = $this->path;
1074
1075 if (method_exists($this, "HEAD")) {
1076 $status = $this->head($options);
1077 } elseif (method_exists($this, "GET")) {
1078 ob_start();
1079 $status = $this->GET($options);
1080 ob_end_clean();
1081 }
1082
1083 if ($status===true) {
1084 $status = "200 OK";
1085 }
1086 if ($status===false) {
1087 $status = "404 Not found";
1088 }
1089
1090 $this->http_status($status);
1091 }
1092
1093 // }}}
1094
1095 // {{{ http_PUT()
1096
1103 public function http_PUT()
1104 {
1105 if ($this->_check_lock_status($this->path)) {
1106 $options = array();
1107 $options["path"] = $this->path;
1108 $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
1109
1110 // get the Content-type
1111 if (isset($_SERVER["CONTENT_TYPE"])) {
1112 // for now we do not support any sort of multipart requests
1113 if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
1114 $this->http_status("501 not implemented");
1115 echo "The service does not support mulipart PUT requests";
1116 return;
1117 }
1118 $options["content_type"] = $_SERVER["CONTENT_TYPE"];
1119 } else {
1120 // default content type if none given
1121 $options["content_type"] = "application/octet-stream";
1122 }
1123
1124 /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
1125 ignore any Content-* (e.g. Content-Range) headers that it
1126 does not understand or implement and MUST return a 501
1127 (Not Implemented) response in such cases."
1128 */
1129 foreach ($_SERVER as $key => $val) {
1130 if (strncmp($key, "HTTP_CONTENT", 11)) {
1131 continue;
1132 }
1133 switch ($key) {
1134 case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
1135 // TODO support this if ext/zlib filters are available
1136 $this->http_status("501 not implemented");
1137 echo "The service does not support '$val' content encoding";
1138 return;
1139
1140 case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
1141 // we assume it is not critical if this one is ignored
1142 // in the actual PUT implementation ...
1143 $options["content_language"] = $value;
1144 break;
1145
1146 case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
1147 /* The meaning of the Content-Location header in PUT
1148 or POST requests is undefined; servers are free
1149 to ignore it in those cases. */
1150 break;
1151
1152 case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
1153 // single byte range requests are supported
1154 // the header format is also specified in RFC 2616 14.16
1155 // TODO we have to ensure that implementations support this or send 501 instead
1156 if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1157 $this->http_status("400 bad request");
1158 echo "The service does only support single byte ranges";
1159 return;
1160 }
1161
1162 $range = array("start"=>$matches[1], "end"=>$matches[2]);
1163 if (is_numeric($matches[3])) {
1164 $range["total_length"] = $matches[3];
1165 }
1166 $option["ranges"][] = $range;
1167
1168 // TODO make sure the implementation supports partial PUT
1169 // this has to be done in advance to avoid data being overwritten
1170 // on implementations that do not support this ...
1171 break;
1172
1173 case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
1174 // TODO: maybe we can just pretend here?
1175 $this->http_status("501 not implemented");
1176 echo "The service does not support content MD5 checksum verification";
1177 return;
1178
1179 case 'HTTP_CONTENT_LENGTH':
1180 // defined on IIS and has the same value as CONTENT_LENGTH
1181 break;
1182
1183 default:
1184 // any other unknown Content-* headers
1185 $this->http_status("501 not implemented");
1186 echo "The service does not support '$key'";
1187 return;
1188 }
1189 }
1190
1191 $options["stream"] = fopen("php://input", "r");
1192
1193 $stat = $this->PUT($options);
1194
1195 if ($stat == false) {
1196 $stat = "403 Forbidden";
1197 } elseif (is_resource($stat) && get_resource_type($stat) == "stream") {
1198 $stream = $stat;
1199
1200 $stat = $options["new"] ? "201 Created" : "204 No Content";
1201
1202 if (!empty($options["ranges"])) {
1203 // TODO multipart support is missing (see also above)
1204 if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) {
1205 $length = $range[0]["end"]-$range[0]["start"]+1;
1206 if (!fwrite($stream, fread($options["stream"], $length))) {
1207 $stat = "403 Forbidden";
1208 }
1209 } else {
1210 $stat = "403 Forbidden";
1211 }
1212 } else {
1213 while (!feof($options["stream"])) {
1214 // BEGIN WebDAV W. Randelshofer explicitly compare with false.
1215 if (false === ($written = fwrite($stream, fread($options["stream"], 4096)))) {
1216 // END WebDAV W. Randelshofer explicitly compare with false.
1217 $stat = "403 Forbidden";
1218 break;
1219 }
1220 $count += $written;
1221 }
1222 }
1223
1224 fclose($stream);
1225 //$this->writelog('PUT wrote '.$written.' bytes');
1226 // BEGIN WebDAV W. Randelshofer finish the put-operation
1227 $this->PUTfinished($options);
1228 // END WebDAV W. Randelshofer finish the put-operation
1229 }
1230
1231 $this->http_status($stat);
1232 } else {
1233 $this->http_status("423 Locked");
1234 }
1235 }
1236
1237 // }}}
1238
1239
1240 // {{{ http_DELETE()
1241
1248 public function http_DELETE()
1249 {
1250 // check RFC 2518 Section 9.2, last paragraph
1251 if (isset($_SERVER["HTTP_DEPTH"])) {
1252 if ($_SERVER["HTTP_DEPTH"] != "infinity") {
1253 $this->http_status("400 Bad Request");
1254 return;
1255 }
1256 }
1257
1258 // check lock status
1259 if ($this->_check_lock_status($this->path)) {
1260 // ok, proceed
1261 $options = array();
1262 $options["path"] = $this->path;
1263
1264 $stat = $this->delete($options);
1265
1266 $this->http_status($stat);
1267 } else {
1268 // sorry, its locked
1269 $this->http_status("423 Locked");
1270 }
1271 }
1272
1273 // }}}
1274
1275 // {{{ http_COPY()
1276
1283 public function http_COPY()
1284 {
1285 // no need to check source lock status here
1286 // destination lock status is always checked by the helper method
1287 $this->_copymove("copy");
1288 }
1289
1290 // }}}
1291
1292 // {{{ http_MOVE()
1293
1300 public function http_MOVE()
1301 {
1302 //$this->writelog('MOVE()');
1303 if ($this->_check_lock_status($this->path)) {
1304 // destination lock status is always checked by the helper method
1305 $this->_copymove("move");
1306 } else {
1307 //$this->writelog('MOVE():423 Locked');
1308 $this->http_status("423 Locked");
1309 }
1310 }
1311
1312 // }}}
1313
1314
1315 // {{{ http_LOCK()
1316
1323 public function http_LOCK()
1324 {
1325 $options = array();
1326 $options["path"] = $this->path;
1327
1328 if (isset($_SERVER['HTTP_DEPTH'])) {
1329 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1330 } else {
1331 $options["depth"] = "infinity";
1332 }
1333
1334 if (isset($_SERVER["HTTP_TIMEOUT"])) {
1335 $options["timeout"] = explode(",", $_SERVER["HTTP_TIMEOUT"]);
1336 }
1337
1338 if (empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
1339 // check if locking is possible
1340 if (!$this->_check_lock_status($this->path)) {
1341 $this->http_status("423 Locked");
1342 return;
1343 }
1344
1345 // refresh lock
1346 $options["update"] = substr($_SERVER['HTTP_IF'], 2, -2);
1347 $stat = $this->lock($options);
1348 } else {
1349 // extract lock request information from request XML payload
1350 $lockinfo = new _parse_lockinfo("php://input");
1351 if (!$lockinfo->success) {
1352 $this->http_status("400 bad request");
1353 }
1354
1355 // check if locking is possible
1356 if (!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) {
1357 $this->http_status("423 Locked");
1358 return;
1359 }
1360
1361 // new lock
1362 $options["scope"] = $lockinfo->lockscope;
1363 $options["type"] = $lockinfo->locktype;
1364 $options["owner"] = $lockinfo->owner;
1365
1366 $options["locktoken"] = $this->_new_locktoken();
1367
1368 $stat = $this->lock($options);
1369 }
1370
1371 if (is_bool($stat)) {
1372 $http_stat = $stat ? "200 OK" : "423 Locked";
1373 } else {
1374 $http_stat = $stat;
1375 }
1376
1377 $this->http_status($http_stat);
1378
1379 if ($http_stat{0} == 2) { // 2xx states are ok
1380 if ($options["timeout"]) {
1381 // more than a million is considered an absolute timestamp
1382 // less is more likely a relative value
1383 if ($options["timeout"]>1000000) {
1384 $timeout = "Second-" . ($options['timeout']-time());
1385 } else {
1386 $timeout = "Second-$options[timeout]";
1387 }
1388 } else {
1389 $timeout = "Infinite";
1390 }
1391 /*
1392 $this->writelog(
1393 'Content-Type: text/xml; charset="utf-8"'
1394 ."Lock-Token: <$options[locktoken]>"
1395 . "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1396 . "<D:prop xmlns:D=\"DAV:\">\n"
1397 . " <D:lockdiscovery>\n"
1398 . " <D:activelock>\n"
1399 . " <D:lockscope><D:$options[scope]/></D:lockscope>\n"
1400 . " <D:locktype><D:$options[type]/></D:locktype>\n"
1401 . " <D:depth>$options[depth]</D:depth>\n"
1402 . " <D:owner>$options[owner]</D:owner>\n"
1403 . " <D:timeout>$timeout</D:timeout>\n"
1404 . " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n"
1405 . " </D:activelock>\n"
1406 . " </D:lockdiscovery>\n"
1407 . "</D:prop>\n\n"
1408 );*/
1409 header('Content-Type: text/xml; charset="utf-8"');
1410 header("Lock-Token: <$options[locktoken]>");
1411 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1412 echo "<D:prop xmlns:D=\"DAV:\">\n";
1413 echo " <D:lockdiscovery>\n";
1414 echo " <D:activelock>\n";
1415 echo " <D:lockscope><D:$options[scope]/></D:lockscope>\n";
1416 echo " <D:locktype><D:$options[type]/></D:locktype>\n";
1417 echo " <D:depth>$options[depth]</D:depth>\n";
1418 echo " <D:owner>$options[owner]</D:owner>\n";
1419 echo " <D:timeout>$timeout</D:timeout>\n";
1420 echo " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n";
1421 echo " </D:activelock>\n";
1422 echo " </D:lockdiscovery>\n";
1423 echo "</D:prop>\n\n";
1424 }
1425 }
1426
1427
1428 // }}}
1429
1430 // {{{ http_UNLOCK()
1431
1438 public function http_UNLOCK()
1439 {
1440 $options = array();
1441 $options["path"] = $this->path;
1442
1443 if (isset($_SERVER['HTTP_DEPTH'])) {
1444 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1445 } else {
1446 $options["depth"] = "infinity";
1447 }
1448
1449 // strip surrounding <>
1450 $options["token"] = substr(trim($_SERVER["HTTP_LOCK_TOKEN"]), 1, -1);
1451 //$this->writelog('http_UNLOCK HTTP_LOCK_TOKEN='.$_SERVER["HTTP_LOCK_TOKEN"]);
1452 // call user method
1453 $stat = $this->unlock($options);
1454
1455 $this->http_status($stat);
1456 }
1457
1458 // }}}
1459
1460 // }}}
1461
1462 // {{{ _copymove()
1463
1464 public function _copymove($what)
1465 {
1466 //$this->writelog('_copymove('.$what.')');
1467 $options = array();
1468 $options["path"] = $this->path;
1469
1470 if (isset($_SERVER["HTTP_DEPTH"])) {
1471 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1472 } else {
1473 $options["depth"] = "infinity";
1474 }
1475 //$this->writelog('_copymove dest='.$_SERVER["HTTP_DESTINATION"]);
1476 extract(parse_url($_SERVER["HTTP_DESTINATION"]));
1477 // BEGIN WebDAV: decode path (bereits in PEAR CVS gefixt)
1478 // We must decode the target path too.
1479 $path = $this->_urldecode($path);
1480 // END Patch WebDAV: decode path
1481 $http_host = $host;
1482 if (isset($port) && $port != 80) {
1483 $http_host.= ":$port";
1484 }
1485
1486 list($http_header_host, $http_header_port) = explode(":", $_SERVER["HTTP_HOST"]);
1487 if (isset($http_header_port) && $http_header_port != 80) {
1488 $http_header_host .= ":" . $http_header_port;
1489 }
1490
1491 if ($http_host == $http_header_host &&
1492 !strncmp(
1493 $_SERVER["SCRIPT_NAME"],
1494 $path,
1495 strlen($_SERVER["SCRIPT_NAME"])
1496 )) {
1497 $options["dest"] = substr($path, strlen($_SERVER["SCRIPT_NAME"]));
1498 //$this->writelog('_copymove() dest='.$options['dest']);
1499 if (!$this->_check_lock_status($options["dest"])) {
1500 //$this->writelog('_copymove():423 Locked');
1501 $this->http_status("423 Locked");
1502 return;
1503 }
1504 //$this->writelog('_copymove() ...');
1505 } else {
1506 $options["dest_url"] = $_SERVER["HTTP_DESTINATION"];
1507 }
1508
1509 // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3
1510 if (isset($_SERVER["HTTP_OVERWRITE"])) {
1511 $options["overwrite"] = $_SERVER["HTTP_OVERWRITE"] == "T";
1512 } else {
1513 $options["overwrite"] = true;
1514 }
1515
1516 $stat = $this->$what($options);
1517 $this->http_status($stat);
1518 }
1519
1520 // }}}
1521
1522 // {{{ _allow()
1523
1530 public function _allow()
1531 {
1532 // OPTIONS is always there
1533 $allow = array("OPTIONS" =>"OPTIONS");
1534
1535 // all other METHODS need both a http_method() wrapper
1536 // and a method() implementation
1537 // the base class supplies wrappers only
1538 foreach (get_class_methods($this) as $method) {
1539 if (!strncmp("http_", $method, 5)) {
1540 $method = strtoupper(substr($method, 5));
1541 if (method_exists($this, $method)) {
1542 $allow[$method] = $method;
1543 }
1544 }
1545 }
1546
1547 // we can emulate a missing HEAD implemetation using GET
1548 if (isset($allow["GET"])) {
1549 $allow["HEAD"] = "HEAD";
1550 }
1551
1552 // no LOCK without checklok()
1553 if (!method_exists($this, "checklock")) {
1554 unset($allow["LOCK"]);
1555 unset($allow["UNLOCK"]);
1556 }
1557
1558 return $allow;
1559 }
1560
1561 // }}}
1562
1571 public function mkprop()
1572 {
1573 $args = func_get_args();
1574 if (count($args) == 3) {
1575 return array("ns" => $args[0],
1576 "name" => $args[1],
1577 "val" => $args[2]);
1578 } else {
1579 return array("ns" => "DAV:",
1580 "name" => $args[0],
1581 "val" => $args[1]);
1582 }
1583 }
1584
1585 // {{{ _check_auth
1586
1593 public function _check_auth()
1594 {
1595 if (method_exists($this, "checkAuth")) {
1596 // PEAR style method name
1597 return $this->checkAuth(
1598 @$_SERVER["AUTH_TYPE"],
1599 @$_SERVER["PHP_AUTH_USER"],
1600 @$_SERVER["PHP_AUTH_PW"]
1601 );
1602 } elseif (method_exists($this, "check_auth")) {
1603 // old (pre 1.0) method name
1604 return $this->check_auth(
1605 @$_SERVER["AUTH_TYPE"],
1606 @$_SERVER["PHP_AUTH_USER"],
1607 @$_SERVER["PHP_AUTH_PW"]
1608 );
1609 } else {
1610 // no method found -> no authentication required
1611 return true;
1612 }
1613 }
1614
1615 // }}}
1616
1617 // {{{ UUID stuff
1618
1625 public function _new_uuid()
1626 {
1627 // use uuid extension from PECL if available
1628 if (function_exists("uuid_create")) {
1629 return uuid_create();
1630 }
1631
1632 // fallback
1633 $uuid = md5(microtime() . getmypid()); // this should be random enough for now
1634
1635 // set variant and version fields for 'true' random uuid
1636 $uuid{12} = "4";
1637 $n = 8 + (ord($uuid{16}) & 3);
1638 $hex = "0123456789abcdef";
1639 $uuid{16} = $hex{$n};
1640
1641 // return formated uuid
1642 return substr($uuid, 0, 8) . "-"
1643 . substr($uuid, 8, 4) . "-"
1644 . substr($uuid, 12, 4) . "-"
1645 . substr($uuid, 16, 4) . "-"
1646 . substr($uuid, 20);
1647 }
1648
1655 public function _new_locktoken()
1656 {
1657 return "opaquelocktoken:" . $this->_new_uuid();
1658 }
1659
1660 // }}}
1661
1662 // {{{ WebDAV If: header parsing
1663
1671 public function _if_header_lexer($string, &$pos)
1672 {
1673 // skip whitespace
1674 while (ctype_space($string{$pos})) {
1675 ++$pos;
1676 }
1677
1678 // already at end of string?
1679 if (strlen($string) <= $pos) {
1680 return false;
1681 }
1682
1683 // get next character
1684 $c = $string{$pos++};
1685
1686 // now it depends on what we found
1687 switch ($c) {
1688 case "<":
1689 // URIs are enclosed in <...>
1690 $pos2 = strpos($string, ">", $pos);
1691 $uri = substr($string, $pos, $pos2 - $pos);
1692 $pos = $pos2 + 1;
1693 return array("URI", $uri);
1694
1695 case "[":
1696 //Etags are enclosed in [...]
1697 if ($string{$pos} == "W") {
1698 $type = "ETAG_WEAK";
1699 $pos += 2;
1700 } else {
1701 $type = "ETAG_STRONG";
1702 }
1703 $pos2 = strpos($string, "]", $pos);
1704 $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
1705 $pos = $pos2 + 1;
1706 return array($type, $etag);
1707
1708 case "N":
1709 // "N" indicates negation
1710 $pos += 2;
1711 return array("NOT", "Not");
1712
1713 default:
1714 // anything else is passed verbatim char by char
1715 return array("CHAR", $c);
1716 }
1717 }
1718
1725 public function _if_header_parser($str)
1726 {
1727 $pos = 0;
1728 $len = strlen($str);
1729
1730 $uris = array();
1731
1732 // parser loop
1733 while ($pos < $len) {
1734 // get next token
1735 $token = $this->_if_header_lexer($str, $pos);
1736
1737 // check for URI
1738 if ($token[0] == "URI") {
1739 $uri = $token[1]; // remember URI
1740 $token = $this->_if_header_lexer($str, $pos); // get next token
1741 } else {
1742 $uri = "";
1743 }
1744
1745 // sanity check
1746 if ($token[0] != "CHAR" || $token[1] != "(") {
1747 return false;
1748 }
1749
1750 $list = array();
1751 $level = 1;
1752 $not = "";
1753 while ($level) {
1754 $token = $this->_if_header_lexer($str, $pos);
1755 if ($token[0] == "NOT") {
1756 $not = "!";
1757 continue;
1758 }
1759 switch ($token[0]) {
1760 case "CHAR":
1761 switch ($token[1]) {
1762 case "(":
1763 $level++;
1764 break;
1765 case ")":
1766 $level--;
1767 break;
1768 default:
1769 return false;
1770 }
1771 break;
1772
1773 case "URI":
1774 $list[] = $not . "<$token[1]>";
1775 break;
1776
1777 case "ETAG_WEAK":
1778 $list[] = $not . "[W/'$token[1]']>";
1779 break;
1780
1781 case "ETAG_STRONG":
1782 $list[] = $not . "['$token[1]']>";
1783 break;
1784
1785 default:
1786 return false;
1787 }
1788 $not = "";
1789 }
1790
1791 if (@is_array($uris[$uri])) {
1792 $uris[$uri] = array_merge($uris[$uri], $list);
1793 } else {
1794 $uris[$uri] = $list;
1795 }
1796 }
1797
1798 return $uris;
1799 }
1800
1811 {
1812 if (isset($_SERVER["HTTP_IF"])) {
1813 $this->_if_header_uris =
1814 $this->_if_header_parser($_SERVER["HTTP_IF"]);
1815
1816 foreach ($this->_if_header_uris as $uri => $conditions) {
1817 if ($uri == "") {
1818 $uri = $this->uri;
1819 }
1820 // all must match
1821 $state = true;
1822 foreach ($conditions as $condition) {
1823 // lock tokens may be free form (RFC2518 6.3)
1824 // but if opaquelocktokens are used (RFC2518 6.4)
1825 // we have to check the format (litmus tests this)
1826 if (!strncmp($condition, "<opaquelocktoken:", strlen("<opaquelocktoken"))) {
1827 if (!preg_match("/^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$/", $condition)) {
1828 return false;
1829 }
1830 }
1831 if (!$this->_check_uri_condition($uri, $condition)) {
1832 $state = false;
1833 break;
1834 }
1835 }
1836
1837 // any match is ok
1838 if ($state == true) {
1839 return true;
1840 }
1841 }
1842 return false;
1843 }
1844 return true;
1845 }
1846
1857 public function _check_uri_condition($uri, $condition)
1858 {
1859 // not really implemented here,
1860 // implementations must override
1861 return true;
1862 }
1863
1864
1871 public function _check_lock_status($path, $exclusive_only = false)
1872 {
1873 // FIXME depth -> ignored for now
1874 if (method_exists($this, "checkLock")) {
1875 // is locked?
1876 $lock = $this->checkLock($path);
1877
1878 // ... and lock is not owned?
1879 if (is_array($lock) && count($lock)) {
1880 // FIXME doesn't check uri restrictions yet
1881 if (!strstr($_SERVER["HTTP_IF"], $lock["token"])) {
1882 if (!$exclusive_only || ($lock["scope"] !== "shared")) {
1883 return false;
1884 }
1885 }
1886 }
1887 }
1888 return true;
1889 }
1890
1891
1892 // }}}
1893
1894
1901 public function lockdiscovery($path)
1902 {
1903 // no lock support without checklock() method
1904 if (!method_exists($this, "checklock")) {
1905 return "";
1906 }
1907
1908 // collect response here
1909 $activelocks = "";
1910
1911 // get checklock() reply
1912 $lock = $this->checklock($path);
1913
1914 // generate <activelock> block for returned data
1915 if (is_array($lock) && count($lock)) {
1916 // check for 'timeout' or 'expires'
1917 if (!empty($lock["expires"])) {
1918 $timeout = "Second-" . ($lock["expires"] - time());
1919 } elseif (!empty($lock["timeout"])) {
1920 $timeout = "Second-$lock[timeout]";
1921 } else {
1922 $timeout = "Infinite";
1923 }
1924
1925 // genreate response block
1926 $activelocks.= "
1927 <D:activelock>
1928 <D:lockscope><D:$lock[scope]/></D:lockscope>
1929 <D:locktype><D:$lock[type]/></D:locktype>
1930 <D:depth>$lock[depth]</D:depth>
1931 <D:owner>$lock[owner]</D:owner>
1932 <D:timeout>$timeout</D:timeout>
1933 <D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
1934 </D:activelock>
1935 ";
1936 }
1937 //$this->writelog('lockdiscovery('.$path.'):'.$activeclocks);
1938
1939 // return generated response
1940 return $activelocks;
1941 }
1942
1949 public function http_status($status)
1950 {
1951 // simplified success case
1952 if ($status === true) {
1953 $status = "200 OK";
1954 }
1955 //$this->writelog('http_status('.$status.')');
1956
1957 // remember status
1958 $this->_http_status = $status;
1959
1960 // generate HTTP status response
1961 header("HTTP/1.1 $status");
1962 header("X-WebDAV-Status: $status", true);
1963 }
1964
1974 public function _urlencode($url)
1975 {
1976 return strtr($url, array(" "=>"%20",
1977 "&"=>"%26",
1978 "<"=>"%3C",
1979 ">"=>"%3E",
1980 ));
1981 }
1982
1991 public function _urldecode($path)
1992 {
1993 // BEGIN WebDAV
1994 // urldecode wrongly replaces '+' characters by ' ' characters.
1995 // We replace '+' into '%2b' before passing the path through urldecode.
1996 //return urldecode($path);
1997 $result =&urldecode(str_replace('+', '%2b', $path));
1998 //$this->writelog('_urldecode('.$path.'):'.$result);
1999 return $result;
2000 // END PATCH WebDAV
2001 }
2002
2009 public function _prop_encode($text)
2010 {
2011 switch (strtolower($this->_prop_encoding)) {
2012 case "utf-8":
2013 return $text;
2014 case "iso-8859-1":
2015 case "iso-8859-15":
2016 case "latin-1":
2017 default:
2018 return utf8_encode($text);
2019 }
2020 }
2021
2028 public function _slashify($path)
2029 {
2030 if ($path[strlen($path)-1] != '/') {
2031 $path = $path . "/";
2032 }
2033 return $path;
2034 }
2035 // BEGIN WebDAV
2042 private function writelog($message)
2043 {
2044 global $DIC;
2045 $log = $DIC['log'];
2046 $ilUser = $DIC['ilUser'];
2047
2048 $log->write(
2049 $ilUser->getLogin()
2050 . ' DAV Server.' . str_replace("\n", ";", $message)
2051 );
2052 }
2053 // END PATCH WebDAV
2054}
$result
$n
Definition: RandomTest.php:85
$size
Definition: RandomTest.php:84
$total
Definition: Utf8Test.php:87
$files
Definition: add-vimline.php:18
if(!isset( $_REQUEST[ 'ReturnTo'])) if(!isset($_REQUEST['AuthId'])) $options
Definition: as_login.php:20
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
An exception for terminatinating execution or to throw for unit testing.
_new_uuid()
generate Unique Universal IDentifier for lock token
Definition: Server.php:1625
http_MOVE()
MOVE method handler.
Definition: Server.php:1300
writelog($message)
Writes a message to the logfile.,.
Definition: Server.php:2042
http_MKCOL()
MKCOL method handler.
Definition: Server.php:840
http_COPY()
COPY method handler.
Definition: Server.php:1283
http_OPTIONS()
GET implementation.
Definition: Server.php:471
_new_locktoken()
create a new opaque lock token as defined in RFC2518
Definition: Server.php:1655
http_GET()
GET method handler.
Definition: Server.php:861
_check_lock_status($path, $exclusive_only=false)
Definition: Server.php:1871
http_PUT()
PUT method handler.
Definition: Server.php:1103
http_LOCK()
LOCK method handler.
Definition: Server.php:1323
_if_header_lexer($string, &$pos)
Definition: Server.php:1671
_check_if_header_conditions()
check if conditions from "If:" headers are meat
Definition: Server.php:1810
_multipart_byterange_header($mimetype=false, $from=false, $to=false, $total=false)
generate separator headers for multipart response
Definition: Server.php:1030
_urlencode($url)
private minimalistic version of PHP urlencode()
Definition: Server.php:1974
_check_uri_condition($uri, $condition)
Check a single URI condition parsed from an if-header.
Definition: Server.php:1857
_prop_encode($text)
UTF-8 encode property values if not already done so.
Definition: Server.php:2009
_if_header_parser($str)
parse If: header
Definition: Server.php:1725
http_PROPPATCH()
PROPPATCH method handler.
Definition: Server.php:783
serveRequest()
Serve WebDAV HTTP request.
Definition: Server.php:125
http_DELETE()
DELETE method handler.
Definition: Server.php:1248
http_UNLOCK()
UNLOCK method handler.
Definition: Server.php:1438
_check_auth()
check authentication if check is implemented
Definition: Server.php:1593
http_HEAD()
HEAD method handler.
Definition: Server.php:1069
_get_ranges(&$options)
parse HTTP Range: header
Definition: Server.php:996
__construct()
Constructor.
Definition: Server.php:108
lockdiscovery($path)
Generate lockdiscovery reply from checklock() result.
Definition: Server.php:1901
http_PROPFIND()
PROPFIND method handler.
Definition: Server.php:505
_urldecode($path)
private version of PHP urldecode
Definition: Server.php:1991
mkprop()
helper for property element creation
Definition: Server.php:1571
_slashify($path)
Slashify - make sure path ends in a slash.
Definition: Server.php:2028
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1949
_allow()
check for implemented HTTP methods
Definition: Server.php:1530
$key
Definition: croninfo.php:18
$end
Definition: saml1-acs.php:18
catch(Exception $e) $message
$stream
PHP stream implementation.
Virtual base class for implementing WebDAV servers.
$type
$url
if(isset($_REQUEST['delete'])) $list
Definition: registry.php:41
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file
global $DIC
Definition: saml.php:7
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$from
$ilUser
Definition: imgupload.php:18
$text
Definition: errorreport.php:18