ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 var $uri;
47
48
55
56
62 var $path;
63
69 var $http_auth_realm = "PHP WebDAV";
70
77
83 var $_if_header_uris = array();
84
90 var $_http_status = "200 OK";
91
97 var $_prop_encoding = "utf-8";
98
99 // }}}
100
101 // {{{ Constructor
102
109 {
110 // PHP messages destroy XML output -> switch them off
111 //ini_set("display_errors", 0);
112 }
113
114 // }}}
115
116 // {{{ ServeRequest()
125 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 {
192 $method = "get";
193 $this->writelog(__METHOD__.': Using head emulation by get.');
194 }
195
196 if (method_exists($this, $wrapper) && ($method == "options" || method_exists($this, $method)))
197 {
198 $this->writelog(__METHOD__.': Calling wrapper: '.$wrapper);
199 $this->$wrapper(); // call method by name
200 }
201 else
202 { // method not found/implemented
203 if ($_SERVER["REQUEST_METHOD"] == "LOCK")
204 {
205 $this->writelog(__METHOD__.': Method not found/implemented. Sending 412');
206 $this->http_status("412 Precondition failed");
207 }
208 else
209 {
210 $this->writelog(__METHOD__.': Method not found/implemented. Sending allowd methods');
211 $this->http_status("405 Method not allowed");
212 header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed
213 }
214 }
215 }
216
217 // }}}
218
219 // {{{ abstract WebDAV methods
220
221 // {{{ GET()
240 /* abstract
241 function GET(&$params)
242 {
243 // dummy entry for PHPDoc
244 }
245 */
246
247 // }}}
248
249 // {{{ PUT()
260 /* abstract
261 function PUT()
262 {
263 // dummy entry for PHPDoc
264 }
265 */
266
267 // }}}
268
269 // {{{ COPY()
270
281 /* abstract
282 function COPY()
283 {
284 // dummy entry for PHPDoc
285 }
286 */
287
288 // }}}
289
290 // {{{ MOVE()
291
302 /* abstract
303 function MOVE()
304 {
305 // dummy entry for PHPDoc
306 }
307 */
308
309 // }}}
310
311 // {{{ DELETE()
312
323 /* abstract
324 function DELETE()
325 {
326 // dummy entry for PHPDoc
327 }
328 */
329 // }}}
330
331 // {{{ PROPFIND()
332
343 /* abstract
344 function PROPFIND()
345 {
346 // dummy entry for PHPDoc
347 }
348 */
349
350 // }}}
351
352 // {{{ PROPPATCH()
353
364 /* abstract
365 function PROPPATCH()
366 {
367 // dummy entry for PHPDoc
368 }
369 */
370 // }}}
371
372 // {{{ LOCK()
373
384 /* abstract
385 function LOCK()
386 {
387 // dummy entry for PHPDoc
388 }
389 */
390 // }}}
391
392 // {{{ UNLOCK()
393
404 /* abstract
405 function UNLOCK()
406 {
407 // dummy entry for PHPDoc
408 }
409 */
410 // }}}
411
412 // }}}
413
414 // {{{ other abstract methods
415
416 // {{{ check_auth()
417
430 /* abstract
431 function checkAuth($type, $username, $password)
432 {
433 // dummy entry for PHPDoc
434 }
435 */
436
437 // }}}
438
439 // {{{ checklock()
440
453 /* abstract
454 function checklock($resource)
455 {
456 // dummy entry for PHPDoc
457 }
458 */
459
460 // }}}
461
462 // }}}
463
464 // {{{ WebDAV HTTP method wrappers
465
466 // {{{ http_OPTIONS()
467
478 function http_OPTIONS()
479 {
480 // Microsoft clients default to the Frontpage protocol
481 // unless we tell them to use WebDAV
482 header("MS-Author-Via: DAV");
483
484 // get allowed methods
485 $allow = $this->_allow();
486
487 // dav header
488 $dav = array(1); // assume we are always dav class 1 compliant
489 if (isset($allow['LOCK'])) {
490 $dav[] = 2; // dav class 2 requires that locking is supported
491 }
492
493 // tell clients what we found
494 $this->http_status("200 OK");
495 header("DAV: " .join("," , $dav));
496 header("Allow: ".join(", ", $allow));
497 $this->writelog(__METHOD__.': dav='.var_export($dav,true).' allow='.var_export($allow,true));
498 header("Content-length: 0");
499 }
500
501 // }}}
502
503
504 // {{{ http_PROPFIND()
505
512 function http_PROPFIND()
513 {
514 $options = Array();
515 $options["path"] = $this->path;
516
517 // search depth from header (default is "infinity)
518 if (isset($_SERVER['HTTP_DEPTH'])) {
519 $options["depth"] = $_SERVER["HTTP_DEPTH"];
520 } else {
521 $options["depth"] = "infinity";
522 }
523
524 // analyze request payload
525 $propinfo = new _parse_propfind("php://input");
526 if (!$propinfo->success) {
527 $this->http_status("400 Error");
528 return;
529 }
530 $options['props'] = $propinfo->props;
531
532 // call user handler
533 $files = array();
534 if (!$this->propfind($options, $files)) {
535 $this->http_status("404 Not Found");
536 return;
537 }
538
539 // collect namespaces here
540 $ns_hash = array();
541
542 // Microsoft Clients need this special namespace for date and time values
543 $ns_defs = "xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
544
545 // now we loop over all returned file entries
546 foreach($files["files"] as $filekey => $file) {
547
548 // nothing to do if no properties were returend for a file
549 if (!isset($file["props"]) || !is_array($file["props"])) {
550 continue;
551 }
552
553 // now loop over all returned properties
554 foreach($file["props"] as $key => $prop) {
555 // as a convenience feature we do not require that user handlers
556 // restrict returned properties to the requested ones
557 // here we strip all unrequested entries out of the response
558
559 switch($options['props']) {
560 case "all":
561 // nothing to remove
562 break;
563
564 case "names":
565 // only the names of all existing properties were requested
566 // so we remove all values
567 unset($files["files"][$filekey]["props"][$key]["val"]);
568 break;
569
570 default:
571 $found = false;
572
573 // search property name in requested properties
574 foreach((array)$options["props"] as $reqprop) {
575 if ( $reqprop["name"] == $prop["name"]
576 && $reqprop["xmlns"] == $prop["ns"]) {
577 $found = true;
578 break;
579 }
580 }
581
582 // unset property and continue with next one if not found/requested
583 if (!$found) {
584 $files["files"][$filekey]["props"][$key]="";
585 continue(2);
586 }
587 break;
588 }
589
590 // namespace handling
591 if (empty($prop["ns"])) continue; // no namespace
592 $ns = $prop["ns"];
593 if ($ns == "DAV:") continue; // default namespace
594 if (isset($ns_hash[$ns])) continue; // already known
595
596 // register namespace
597 $ns_name = "ns".(count($ns_hash) + 1);
598 $ns_hash[$ns] = $ns_name;
599 $ns_defs .= " xmlns:$ns_name=\"$ns\"";
600 }
601
602 // we also need to add empty entries for properties that were requested
603 // but for which no values where returned by the user handler
604 if (is_array($options['props'])) {
605 foreach($options["props"] as $reqprop) {
606 if($reqprop['name']=="") continue; // skip empty entries
607
608 $found = false;
609
610 // check if property exists in result
611 foreach($file["props"] as $prop) {
612 if ( $reqprop["name"] == $prop["name"]
613 && $reqprop["xmlns"] == $prop["ns"]) {
614 $found = true;
615 break;
616 }
617 }
618
619 if (!$found) {
620 if($reqprop["xmlns"]==="DAV:" && $reqprop["name"]==="lockdiscovery") {
621 // lockdiscovery is handled by the base class
622 $files["files"][$filekey]["props"][]
623 = $this->mkprop("DAV:",
624 "lockdiscovery" ,
625 $this->lockdiscovery($files["files"][$filekey]['path']));
626 } else {
627 // add empty value for this property
628 $files["files"][$filekey]["noprops"][] =
629 $this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
630
631 // register property namespace if not known yet
632 if ($reqprop["xmlns"] != "DAV:" && !isset($ns_hash[$reqprop["xmlns"]])) {
633 $ns_name = "ns".(count($ns_hash) + 1);
634 $ns_hash[$reqprop["xmlns"]] = $ns_name;
635 $ns_defs .= " xmlns:$ns_name=\"$reqprop[xmlns]\"";
636 }
637 }
638 }
639 }
640 }
641 }
642
643 // now we generate the reply header ...
644 $this->http_status("207 Multi-Status");
645 header('Content-Type: text/xml; charset="utf-8"');
646
647 // ... and payload
648 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
649 echo "<D:multistatus xmlns:D=\"DAV:\">\n";
650
651 foreach($files["files"] as $file) {
652 // ignore empty or incomplete entries
653 if(!is_array($file) || empty($file) || !isset($file["path"])) continue;
654 $path = $file['path'];
655 if(!is_string($path) || $path==="") continue;
656
657 echo " <D:response $ns_defs>\n";
658
659 // BEGIN WebDAV W. Randelshofer Don't slashify path because it confuses Mac OS X
660 //$href = $this->_slashify($_SERVER['SCRIPT_NAME'] . $path);
661 $href = $_SERVER['SCRIPT_NAME'] . $path;
662 //END PATCH WebDAV W. Randelshofer
663
664 echo " <D:href>$href</D:href>\n";
665
666 // report all found properties and their values (if any)
667 if (isset($file["props"]) && is_array($file["props"])) {
668 echo " <D:propstat>\n";
669 echo " <D:prop>\n";
670
671 foreach($file["props"] as $key => $prop) {
672
673 if (!is_array($prop)) continue;
674 if (!isset($prop["name"])) continue;
675 if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) {
676 // empty properties (cannot use empty() for check as "0" is a legal value here)
677 if($prop["ns"]=="DAV:") {
678 echo " <D:$prop[name]/>\n";
679 } else if(!empty($prop["ns"])) {
680 echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n";
681 } else {
682 echo " <$prop[name] xmlns=\"\"/>";
683 }
684 } else if ($prop["ns"] == "DAV:") {
685 // some WebDAV properties need special treatment
686 switch ($prop["name"]) {
687 case "creationdate":
688 echo " <D:creationdate ns0:dt=\"dateTime.tz\">"
689 // BEGIN WebDAV W. Randelshofer
690 . gmdate("Y-m-d\\TH:i:s\\Z",$prop['val'])
691 // . gmdate("D, d M Y H:i:s ", $prop['val'])
692 // END PATCH WebDAV W. Randelshofer
693 . "</D:creationdate>\n";
694 break;
695 case "getlastmodified":
696 echo " <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"
697 . gmdate("D, d M Y H:i:s ", $prop['val'])
698 . "GMT</D:getlastmodified>\n";
699 break;
700 case "resourcetype":
701 echo " <D:resourcetype><D:$prop[val]/></D:resourcetype>\n";
702 break;
703 case "supportedlock":
704 echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
705 break;
706 case "lockdiscovery":
707 echo " <D:lockdiscovery>\n";
708 echo $prop["val"];
709 echo " </D:lockdiscovery>\n";
710 break;
711 default:
712 echo " <D:$prop[name]>"
713 . $this->_prop_encode(htmlspecialchars($prop['val']))
714 . "</D:$prop[name]>\n";
715 break;
716 }
717 } else {
718 // properties from namespaces != "DAV:" or without any namespace
719 if ($prop["ns"]) {
720 echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
721 . $this->_prop_encode(htmlspecialchars($prop['val']))
722 . "</" . $ns_hash[$prop["ns"]] . ":$prop[name]>\n";
723 } else {
724 echo " <$prop[name] xmlns=\"\">"
725 . $this->_prop_encode(htmlspecialchars($prop['val']))
726 . "</$prop[name]>\n";
727 }
728 }
729 }
730
731 echo " </D:prop>\n";
732 echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
733 echo " </D:propstat>\n";
734 }
735
736 // now report all properties requested but not found
737 if (isset($file["noprops"])) {
738 echo " <D:propstat>\n";
739 echo " <D:prop>\n";
740
741 foreach($file["noprops"] as $key => $prop) {
742 if ($prop["ns"] == "DAV:") {
743 echo " <D:$prop[name]/>\n";
744 } else if ($prop["ns"] == "") {
745 echo " <$prop[name] xmlns=\"\"/>\n";
746 } else {
747 echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n";
748 }
749 }
750
751 echo " </D:prop>\n";
752 echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
753 echo " </D:propstat>\n";
754 }
755
756 echo " </D:response>\n";
757 }
758
759 echo "</D:multistatus>\n";
760 }
761
762
763 // }}}
764
765 // {{{ http_PROPPATCH()
766
773 function http_PROPPATCH()
774 {
775 if($this->_check_lock_status($this->path)) {
776 $options = Array();
777 $options["path"] = $this->path;
778
779 $propinfo = new _parse_proppatch("php://input");
780
781 if (!$propinfo->success) {
782 $this->http_status("400 Error");
783 return;
784 }
785
786 $options['props'] = $propinfo->props;
787
788 $responsedescr = $this->proppatch($options);
789
790 $this->http_status("207 Multi-Status");
791 header('Content-Type: text/xml; charset="utf-8"');
792
793 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
794
795 echo "<D:multistatus xmlns:D=\"DAV:\">\n";
796 echo " <D:response>\n";
797 echo " <D:href>".$this->_urlencode($_SERVER["SCRIPT_NAME"].$this->path)."</D:href>\n";
798
799 foreach($options["props"] as $prop) {
800 echo " <D:propstat>\n";
801 echo " <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
802 echo " <D:status>HTTP/1.1 $prop[status]</D:status>\n";
803 echo " </D:propstat>\n";
804 }
805
806 if ($responsedescr) {
807 echo " <D:responsedescription>".
808 $this->_prop_encode(htmlspecialchars($responsedescr)).
809 "</D:responsedescription>\n";
810 }
811
812 echo " </D:response>\n";
813 echo "</D:multistatus>\n";
814 } else {
815 $this->http_status("423 Locked");
816 }
817 }
818
819 // }}}
820
821
822 // {{{ http_MKCOL()
823
830 function http_MKCOL()
831 {
832 $options = Array();
833 $options["path"] = $this->path;
834
835 $stat = $this->mkcol($options);
836
837 $this->http_status($stat);
838 }
839
840 // }}}
841
842
843 // {{{ http_GET()
844
851 function http_GET()
852 {
853 // TODO check for invalid stream
854 $options = Array();
855 $options["path"] = $this->path;
856
857 $this->_get_ranges($options);
858
859 if (true === ($status = $this->get($options))) {
860 if (!headers_sent()) {
861 $status = "200 OK";
862
863 if (!isset($options['mimetype'])) {
864 $options['mimetype'] = "application/octet-stream";
865 }
866 header("Content-type: $options[mimetype]");
867
868 if (isset($options['mtime'])) {
869 header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT");
870 }
871
872 if (isset($options['stream'])) {
873 // GET handler returned a stream
874 if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) {
875 // partial request and stream is seekable
876
877 if (count($options['ranges']) === 1) {
878 $range = $options['ranges'][0];
879
880 if (isset($range['start'])) {
881 fseek($options['stream'], $range['start'], SEEK_SET);
882 if (feof($options['stream'])) {
883 $this->http_status("416 Requested range not satisfiable");
884 exit;
885 }
886
887 if (isset($range['end'])) {
888 $size = $range['end']-$range['start']+1;
889 $this->http_status("206 partial");
890 header("Content-length: $size");
891 header("Content-range: $range[start]-$range[end]/"
892 . (isset($options['size']) ? $options['size'] : "*"));
893 while ($size && !feof($options['stream'])) {
894 $buffer = fread($options['stream'], 4096);
895 $size -= strlen($buffer);
896 echo $buffer;
897 }
898 } else {
899 $this->http_status("206 partial");
900 if (isset($options['size'])) {
901 header("Content-length: ".($options['size'] - $range['start']));
902 header("Content-range: $start-$end/"
903 . (isset($options['size']) ? $options['size'] : "*"));
904 }
905 fpassthru($options['stream']);
906 }
907 } else {
908 header("Content-length: ".$range['last']);
909 fseek($options['stream'], -$range['last'], SEEK_END);
910 fpassthru($options['stream']);
911 }
912 } else {
913 $this->_multipart_byterange_header(); // init multipart
914 foreach ($options['ranges'] as $range) {
915 // TODO what if size unknown? 500?
916 if (isset($range['start'])) {
917 $from = $range['start'];
918 $to = !empty($range['end']) ? $range['end'] : $options['size']-1;
919 } else {
920 $from = $options['size'] - $range['last']-1;
921 $to = $options['size'] -1;
922 }
923 $total = isset($options['size']) ? $options['size'] : "*";
924 $size = $to - $from + 1;
925 $this->_multipart_byterange_header($options['mimetype'], $from, $to, $total);
926
927
928 fseek($options['stream'], $start, SEEK_SET);
929 while ($size && !feof($options['stream'])) {
930 $buffer = fread($options['stream'], 4096);
931 $size -= strlen($buffer);
932 echo $buffer;
933 }
934 }
935 $this->_multipart_byterange_header(); // end multipart
936 }
937 } else {
938 // normal request or stream isn't seekable, return full content
939 if (isset($options['size'])) {
940 header("Content-length: ".$options['size']);
941 }
942
943 // BEGIN WebDAV W. Randelshofer
944 // fpassthru apparently only delivers up to 2 million bytes.
945 // use fread instead
946 //fpassthru($options['stream']);
947 while (! feof($options['stream'])) {
948 $buffer = fread($options['stream'], 4096);
949 echo $buffer;
950 }
951 // END PATCH WebDAV W. Randelshofer
952
953 return; // no more headers
954 }
955 } elseif (isset($options['data'])) {
956 if (is_array($options['data'])) {
957 // reply to partial request
958 } else {
959 header("Content-length: ".strlen($options['data']));
960 echo $options['data'];
961 }
962 }
963 }
964 }
965
966 if (false === $status) {
967 // BEGIN WebDAV Randelshofer
968 $status = '404 Not Found';
969 //$this->http_status("404 not found");
970 // END PATCH WebDAV Randelshofer
971 }
972
973 if (!headers_sent()) {
974 // TODO: check setting of headers in various code pathes above
975 $this->http_status("$status");
976 }
977 }
978
979
987 {
988 // process Range: header if present
989 if (isset($_SERVER['HTTP_RANGE'])) {
990
991 // we only support standard "bytes" range specifications for now
992 if (ereg("bytes[[:space:]]*=[[:space:]]*(.+)", $_SERVER['HTTP_RANGE'], $matches)) {
993 $options["ranges"] = array();
994
995 // ranges are comma separated
996 foreach (explode(",", $matches[1]) as $range) {
997 // ranges are either from-to pairs or just end positions
998 list($start, $end) = explode("-", $range);
999 $options["ranges"][] = ($start==="")
1000 ? array("last"=>$end)
1001 : array("start"=>$start, "end"=>$end);
1002 }
1003 }
1004 }
1005 }
1006
1020 function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false)
1021 {
1022 if ($mimetype === false) {
1023 if (!isset($this->multipart_separator)) {
1024 // initial
1025
1026 // a little naive, this sequence *might* be part of the content
1027 // but it's really not likely and rather expensive to check
1028 $this->multipart_separator = "SEPARATOR_".md5(microtime());
1029
1030 // generate HTTP header
1031 header("Content-type: multipart/byteranges; boundary=".$this->multipart_separator);
1032 } else {
1033 // final
1034
1035 // generate closing multipart sequence
1036 echo "\n--{$this->multipart_separator}--";
1037 }
1038 } else {
1039 // generate separator and header for next part
1040 echo "\n--{$this->multipart_separator}\n";
1041 echo "Content-type: $mimetype\n";
1042 echo "Content-range: $from-$to/". ($total === false ? "*" : $total);
1043 echo "\n\n";
1044 }
1045 }
1046
1047
1048
1049 // }}}
1050
1051 // {{{ http_HEAD()
1052
1059 function http_HEAD()
1060 {
1061 $status = false;
1062 $options = Array();
1063 $options["path"] = $this->path;
1064
1065 if (method_exists($this, "HEAD")) {
1066 $status = $this->head($options);
1067 } else if (method_exists($this, "GET")) {
1068 ob_start();
1069 $status = $this->GET($options);
1070 ob_end_clean();
1071 }
1072
1073 if($status===true) $status = "200 OK";
1074 if($status===false) $status = "404 Not found";
1075
1076 $this->http_status($status);
1077 }
1078
1079 // }}}
1080
1081 // {{{ http_PUT()
1082
1089 function http_PUT()
1090 {
1091 if ($this->_check_lock_status($this->path)) {
1092 $options = Array();
1093 $options["path"] = $this->path;
1094 $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
1095
1096 // get the Content-type
1097 if (isset($_SERVER["CONTENT_TYPE"])) {
1098 // for now we do not support any sort of multipart requests
1099 if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
1100 $this->http_status("501 not implemented");
1101 echo "The service does not support mulipart PUT requests";
1102 return;
1103 }
1104 $options["content_type"] = $_SERVER["CONTENT_TYPE"];
1105 } else {
1106 // default content type if none given
1107 $options["content_type"] = "application/octet-stream";
1108 }
1109
1110 /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
1111 ignore any Content-* (e.g. Content-Range) headers that it
1112 does not understand or implement and MUST return a 501
1113 (Not Implemented) response in such cases."
1114 */
1115 foreach ($_SERVER as $key => $val) {
1116 if (strncmp($key, "HTTP_CONTENT", 11)) continue;
1117 switch ($key) {
1118 case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
1119 // TODO support this if ext/zlib filters are available
1120 $this->http_status("501 not implemented");
1121 echo "The service does not support '$val' content encoding";
1122 return;
1123
1124 case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
1125 // we assume it is not critical if this one is ignored
1126 // in the actual PUT implementation ...
1127 $options["content_language"] = $value;
1128 break;
1129
1130 case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
1131 /* The meaning of the Content-Location header in PUT
1132 or POST requests is undefined; servers are free
1133 to ignore it in those cases. */
1134 break;
1135
1136 case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
1137 // single byte range requests are supported
1138 // the header format is also specified in RFC 2616 14.16
1139 // TODO we have to ensure that implementations support this or send 501 instead
1140 if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1141 $this->http_status("400 bad request");
1142 echo "The service does only support single byte ranges";
1143 return;
1144 }
1145
1146 $range = array("start"=>$matches[1], "end"=>$matches[2]);
1147 if (is_numeric($matches[3])) {
1148 $range["total_length"] = $matches[3];
1149 }
1150 $option["ranges"][] = $range;
1151
1152 // TODO make sure the implementation supports partial PUT
1153 // this has to be done in advance to avoid data being overwritten
1154 // on implementations that do not support this ...
1155 break;
1156
1157 case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
1158 // TODO: maybe we can just pretend here?
1159 $this->http_status("501 not implemented");
1160 echo "The service does not support content MD5 checksum verification";
1161 return;
1162
1163 case 'HTTP_CONTENT_LENGTH':
1164 // defined on IIS and has the same value as CONTENT_LENGTH
1165 break;
1166
1167 default:
1168 // any other unknown Content-* headers
1169 $this->http_status("501 not implemented");
1170 echo "The service does not support '$key'";
1171 return;
1172 }
1173 }
1174
1175 $options["stream"] = fopen("php://input", "r");
1176
1177 $stat = $this->PUT($options);
1178
1179 if ($stat == false) {
1180 $stat = "403 Forbidden";
1181 } else if (is_resource($stat) && get_resource_type($stat) == "stream") {
1182 $stream = $stat;
1183
1184 $stat = $options["new"] ? "201 Created" : "204 No Content";
1185
1186 if (!empty($options["ranges"])) {
1187 // TODO multipart support is missing (see also above)
1188 if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) {
1189 $length = $range[0]["end"]-$range[0]["start"]+1;
1190 if (!fwrite($stream, fread($options["stream"], $length))) {
1191 $stat = "403 Forbidden";
1192 }
1193 } else {
1194 $stat = "403 Forbidden";
1195 }
1196 } else {
1197 while (!feof($options["stream"])) {
1198 // BEGIN WebDAV W. Randelshofer explicitly compare with false.
1199 if (false === ($written = fwrite($stream, fread($options["stream"], 4096)))) {
1200 // END WebDAV W. Randelshofer explicitly compare with false.
1201 $stat = "403 Forbidden";
1202 break;
1203 }
1204 $count += $written;
1205 }
1206 }
1207
1208 fclose($stream);
1209 //$this->writelog('PUT wrote '.$written.' bytes');
1210 // BEGIN WebDAV W. Randelshofer finish the put-operation
1211 $this->PUTfinished($options);
1212 // END WebDAV W. Randelshofer finish the put-operation
1213 }
1214
1215 $this->http_status($stat);
1216 } else {
1217 $this->http_status("423 Locked");
1218 }
1219 }
1220
1221 // }}}
1222
1223
1224 // {{{ http_DELETE()
1225
1232 function http_DELETE()
1233 {
1234 // check RFC 2518 Section 9.2, last paragraph
1235 if (isset($_SERVER["HTTP_DEPTH"])) {
1236 if ($_SERVER["HTTP_DEPTH"] != "infinity") {
1237 $this->http_status("400 Bad Request");
1238 return;
1239 }
1240 }
1241
1242 // check lock status
1243 if ($this->_check_lock_status($this->path)) {
1244 // ok, proceed
1245 $options = Array();
1246 $options["path"] = $this->path;
1247
1248 $stat = $this->delete($options);
1249
1250 $this->http_status($stat);
1251 } else {
1252 // sorry, its locked
1253 $this->http_status("423 Locked");
1254 }
1255 }
1256
1257 // }}}
1258
1259 // {{{ http_COPY()
1260
1267 function http_COPY()
1268 {
1269 // no need to check source lock status here
1270 // destination lock status is always checked by the helper method
1271 $this->_copymove("copy");
1272 }
1273
1274 // }}}
1275
1276 // {{{ http_MOVE()
1277
1284 function http_MOVE()
1285 {
1286 //$this->writelog('MOVE()');
1287 if ($this->_check_lock_status($this->path)) {
1288 // destination lock status is always checked by the helper method
1289 $this->_copymove("move");
1290 } else {
1291 //$this->writelog('MOVE():423 Locked');
1292 $this->http_status("423 Locked");
1293 }
1294 }
1295
1296 // }}}
1297
1298
1299 // {{{ http_LOCK()
1300
1307 function http_LOCK()
1308 {
1309 $options = Array();
1310 $options["path"] = $this->path;
1311
1312 if (isset($_SERVER['HTTP_DEPTH'])) {
1313 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1314 } else {
1315 $options["depth"] = "infinity";
1316 }
1317
1318 if (isset($_SERVER["HTTP_TIMEOUT"])) {
1319 $options["timeout"] = explode(",", $_SERVER["HTTP_TIMEOUT"]);
1320 }
1321
1322 if(empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
1323 // check if locking is possible
1324 if(!$this->_check_lock_status($this->path)) {
1325 $this->http_status("423 Locked");
1326 return;
1327 }
1328
1329 // refresh lock
1330 $options["update"] = substr($_SERVER['HTTP_IF'], 2, -2);
1331 $stat = $this->lock($options);
1332 } else {
1333 // extract lock request information from request XML payload
1334 $lockinfo = new _parse_lockinfo("php://input");
1335 if (!$lockinfo->success) {
1336 $this->http_status("400 bad request");
1337 }
1338
1339 // check if locking is possible
1340 if(!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) {
1341 $this->http_status("423 Locked");
1342 return;
1343 }
1344
1345 // new lock
1346 $options["scope"] = $lockinfo->lockscope;
1347 $options["type"] = $lockinfo->locktype;
1348 $options["owner"] = $lockinfo->owner;
1349
1350 $options["locktoken"] = $this->_new_locktoken();
1351
1352 $stat = $this->lock($options);
1353 }
1354
1355 if(is_bool($stat)) {
1356 $http_stat = $stat ? "200 OK" : "423 Locked";
1357 } else {
1358 $http_stat = $stat;
1359 }
1360
1361 $this->http_status($http_stat);
1362
1363 if ($http_stat{0} == 2) { // 2xx states are ok
1364 if($options["timeout"]) {
1365 // more than a million is considered an absolute timestamp
1366 // less is more likely a relative value
1367 if($options["timeout"]>1000000) {
1368 $timeout = "Second-".($options['timeout']-time());
1369 } else {
1370 $timeout = "Second-$options[timeout]";
1371 }
1372 } else {
1373 $timeout = "Infinite";
1374 }
1375 /*
1376 $this->writelog(
1377 'Content-Type: text/xml; charset="utf-8"'
1378 ."Lock-Token: <$options[locktoken]>"
1379 . "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1380 . "<D:prop xmlns:D=\"DAV:\">\n"
1381 . " <D:lockdiscovery>\n"
1382 . " <D:activelock>\n"
1383 . " <D:lockscope><D:$options[scope]/></D:lockscope>\n"
1384 . " <D:locktype><D:$options[type]/></D:locktype>\n"
1385 . " <D:depth>$options[depth]</D:depth>\n"
1386 . " <D:owner>$options[owner]</D:owner>\n"
1387 . " <D:timeout>$timeout</D:timeout>\n"
1388 . " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n"
1389 . " </D:activelock>\n"
1390 . " </D:lockdiscovery>\n"
1391 . "</D:prop>\n\n"
1392 );*/
1393 header('Content-Type: text/xml; charset="utf-8"');
1394 header("Lock-Token: <$options[locktoken]>");
1395 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1396 echo "<D:prop xmlns:D=\"DAV:\">\n";
1397 echo " <D:lockdiscovery>\n";
1398 echo " <D:activelock>\n";
1399 echo " <D:lockscope><D:$options[scope]/></D:lockscope>\n";
1400 echo " <D:locktype><D:$options[type]/></D:locktype>\n";
1401 echo " <D:depth>$options[depth]</D:depth>\n";
1402 echo " <D:owner>$options[owner]</D:owner>\n";
1403 echo " <D:timeout>$timeout</D:timeout>\n";
1404 echo " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n";
1405 echo " </D:activelock>\n";
1406 echo " </D:lockdiscovery>\n";
1407 echo "</D:prop>\n\n";
1408 }
1409 }
1410
1411
1412 // }}}
1413
1414 // {{{ http_UNLOCK()
1415
1422 function http_UNLOCK()
1423 {
1424 $options = Array();
1425 $options["path"] = $this->path;
1426
1427 if (isset($_SERVER['HTTP_DEPTH'])) {
1428 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1429 } else {
1430 $options["depth"] = "infinity";
1431 }
1432
1433 // strip surrounding <>
1434 $options["token"] = substr(trim($_SERVER["HTTP_LOCK_TOKEN"]), 1, -1);
1435//$this->writelog('http_UNLOCK HTTP_LOCK_TOKEN='.$_SERVER["HTTP_LOCK_TOKEN"]);
1436 // call user method
1437 $stat = $this->unlock($options);
1438
1439 $this->http_status($stat);
1440 }
1441
1442 // }}}
1443
1444 // }}}
1445
1446 // {{{ _copymove()
1447
1448 function _copymove($what)
1449 {
1450 //$this->writelog('_copymove('.$what.')');
1451 $options = Array();
1452 $options["path"] = $this->path;
1453
1454 if (isset($_SERVER["HTTP_DEPTH"])) {
1455 $options["depth"] = $_SERVER["HTTP_DEPTH"];
1456 } else {
1457 $options["depth"] = "infinity";
1458 }
1459//$this->writelog('_copymove dest='.$_SERVER["HTTP_DESTINATION"]);
1460 extract(parse_url($_SERVER["HTTP_DESTINATION"]));
1461 // BEGIN WebDAV: decode path (bereits in PEAR CVS gefixt)
1462 // We must decode the target path too.
1463 $path = $this->_urldecode($path);
1464 // END Patch WebDAV: decode path
1465 $http_host = $host;
1466 if (isset($port) && $port != 80)
1467 $http_host.= ":$port";
1468
1469 list($http_header_host,$http_header_port) = explode(":",$_SERVER["HTTP_HOST"]);
1470 if (isset($http_header_port) && $http_header_port != 80) {
1471 $http_header_host .= ":".$http_header_port;
1472 }
1473
1474 if ($http_host == $http_header_host &&
1475 !strncmp($_SERVER["SCRIPT_NAME"], $path,
1476 strlen($_SERVER["SCRIPT_NAME"]))) {
1477 $options["dest"] = substr($path, strlen($_SERVER["SCRIPT_NAME"]));
1478 //$this->writelog('_copymove() dest='.$options['dest']);
1479 if (!$this->_check_lock_status($options["dest"])) {
1480 //$this->writelog('_copymove():423 Locked');
1481 $this->http_status("423 Locked");
1482 return;
1483 }
1484 //$this->writelog('_copymove() ...');
1485
1486 } else {
1487 $options["dest_url"] = $_SERVER["HTTP_DESTINATION"];
1488 }
1489
1490 // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3
1491 if (isset($_SERVER["HTTP_OVERWRITE"])) {
1492 $options["overwrite"] = $_SERVER["HTTP_OVERWRITE"] == "T";
1493 } else {
1494 $options["overwrite"] = true;
1495 }
1496
1497 $stat = $this->$what($options);
1498 $this->http_status($stat);
1499 }
1500
1501 // }}}
1502
1503 // {{{ _allow()
1504
1511 function _allow()
1512 {
1513 // OPTIONS is always there
1514 $allow = array("OPTIONS" =>"OPTIONS");
1515
1516 // all other METHODS need both a http_method() wrapper
1517 // and a method() implementation
1518 // the base class supplies wrappers only
1519 foreach(get_class_methods($this) as $method) {
1520 if (!strncmp("http_", $method, 5)) {
1521 $method = strtoupper(substr($method, 5));
1522 if (method_exists($this, $method)) {
1523 $allow[$method] = $method;
1524 }
1525 }
1526 }
1527
1528 // we can emulate a missing HEAD implemetation using GET
1529 if (isset($allow["GET"]))
1530 $allow["HEAD"] = "HEAD";
1531
1532 // no LOCK without checklok()
1533 if (!method_exists($this, "checklock")) {
1534 unset($allow["LOCK"]);
1535 unset($allow["UNLOCK"]);
1536 }
1537
1538 return $allow;
1539 }
1540
1541 // }}}
1542
1551 function mkprop()
1552 {
1553 $args = func_get_args();
1554 if (count($args) == 3) {
1555 return array("ns" => $args[0],
1556 "name" => $args[1],
1557 "val" => $args[2]);
1558 } else {
1559 return array("ns" => "DAV:",
1560 "name" => $args[0],
1561 "val" => $args[1]);
1562 }
1563 }
1564
1565 // {{{ _check_auth
1566
1573 function _check_auth()
1574 {
1575 if (method_exists($this, "checkAuth")) {
1576 // PEAR style method name
1577 return $this->checkAuth(@$_SERVER["AUTH_TYPE"],
1578 @$_SERVER["PHP_AUTH_USER"],
1579 @$_SERVER["PHP_AUTH_PW"]);
1580 } else if (method_exists($this, "check_auth")) {
1581 // old (pre 1.0) method name
1582 return $this->check_auth(@$_SERVER["AUTH_TYPE"],
1583 @$_SERVER["PHP_AUTH_USER"],
1584 @$_SERVER["PHP_AUTH_PW"]);
1585 } else {
1586 // no method found -> no authentication required
1587 return true;
1588 }
1589 }
1590
1591 // }}}
1592
1593 // {{{ UUID stuff
1594
1601 function _new_uuid()
1602 {
1603 // use uuid extension from PECL if available
1604 if (function_exists("uuid_create")) {
1605 return uuid_create();
1606 }
1607
1608 // fallback
1609 $uuid = md5(microtime().getmypid()); // this should be random enough for now
1610
1611 // set variant and version fields for 'true' random uuid
1612 $uuid{12} = "4";
1613 $n = 8 + (ord($uuid{16}) & 3);
1614 $hex = "0123456789abcdef";
1615 $uuid{16} = $hex{$n};
1616
1617 // return formated uuid
1618 return substr($uuid, 0, 8)."-"
1619 . substr($uuid, 8, 4)."-"
1620 . substr($uuid, 12, 4)."-"
1621 . substr($uuid, 16, 4)."-"
1622 . substr($uuid, 20);
1623 }
1624
1631 function _new_locktoken()
1632 {
1633 return "opaquelocktoken:".$this->_new_uuid();
1634 }
1635
1636 // }}}
1637
1638 // {{{ WebDAV If: header parsing
1639
1647 function _if_header_lexer($string, &$pos)
1648 {
1649 // skip whitespace
1650 while (ctype_space($string{$pos})) {
1651 ++$pos;
1652 }
1653
1654 // already at end of string?
1655 if (strlen($string) <= $pos) {
1656 return false;
1657 }
1658
1659 // get next character
1660 $c = $string{$pos++};
1661
1662 // now it depends on what we found
1663 switch ($c) {
1664 case "<":
1665 // URIs are enclosed in <...>
1666 $pos2 = strpos($string, ">", $pos);
1667 $uri = substr($string, $pos, $pos2 - $pos);
1668 $pos = $pos2 + 1;
1669 return array("URI", $uri);
1670
1671 case "[":
1672 //Etags are enclosed in [...]
1673 if ($string{$pos} == "W") {
1674 $type = "ETAG_WEAK";
1675 $pos += 2;
1676 } else {
1677 $type = "ETAG_STRONG";
1678 }
1679 $pos2 = strpos($string, "]", $pos);
1680 $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
1681 $pos = $pos2 + 1;
1682 return array($type, $etag);
1683
1684 case "N":
1685 // "N" indicates negation
1686 $pos += 2;
1687 return array("NOT", "Not");
1688
1689 default:
1690 // anything else is passed verbatim char by char
1691 return array("CHAR", $c);
1692 }
1693 }
1694
1701 function _if_header_parser($str)
1702 {
1703 $pos = 0;
1704 $len = strlen($str);
1705
1706 $uris = array();
1707
1708 // parser loop
1709 while ($pos < $len) {
1710 // get next token
1711 $token = $this->_if_header_lexer($str, $pos);
1712
1713 // check for URI
1714 if ($token[0] == "URI") {
1715 $uri = $token[1]; // remember URI
1716 $token = $this->_if_header_lexer($str, $pos); // get next token
1717 } else {
1718 $uri = "";
1719 }
1720
1721 // sanity check
1722 if ($token[0] != "CHAR" || $token[1] != "(") {
1723 return false;
1724 }
1725
1726 $list = array();
1727 $level = 1;
1728 $not = "";
1729 while ($level) {
1730 $token = $this->_if_header_lexer($str, $pos);
1731 if ($token[0] == "NOT") {
1732 $not = "!";
1733 continue;
1734 }
1735 switch ($token[0]) {
1736 case "CHAR":
1737 switch ($token[1]) {
1738 case "(":
1739 $level++;
1740 break;
1741 case ")":
1742 $level--;
1743 break;
1744 default:
1745 return false;
1746 }
1747 break;
1748
1749 case "URI":
1750 $list[] = $not."<$token[1]>";
1751 break;
1752
1753 case "ETAG_WEAK":
1754 $list[] = $not."[W/'$token[1]']>";
1755 break;
1756
1757 case "ETAG_STRONG":
1758 $list[] = $not."['$token[1]']>";
1759 break;
1760
1761 default:
1762 return false;
1763 }
1764 $not = "";
1765 }
1766
1767 if (@is_array($uris[$uri])) {
1768 $uris[$uri] = array_merge($uris[$uri],$list);
1769 } else {
1770 $uris[$uri] = $list;
1771 }
1772 }
1773
1774 return $uris;
1775 }
1776
1787 {
1788 if (isset($_SERVER["HTTP_IF"])) {
1789 $this->_if_header_uris =
1790 $this->_if_header_parser($_SERVER["HTTP_IF"]);
1791
1792 foreach($this->_if_header_uris as $uri => $conditions) {
1793 if ($uri == "") {
1794 $uri = $this->uri;
1795 }
1796 // all must match
1797 $state = true;
1798 foreach($conditions as $condition) {
1799 // lock tokens may be free form (RFC2518 6.3)
1800 // but if opaquelocktokens are used (RFC2518 6.4)
1801 // we have to check the format (litmus tests this)
1802 if (!strncmp($condition, "<opaquelocktoken:", strlen("<opaquelocktoken"))) {
1803 if (!ereg("^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$", $condition)) {
1804 return false;
1805 }
1806 }
1807 if (!$this->_check_uri_condition($uri, $condition)) {
1808 $state = false;
1809 break;
1810 }
1811 }
1812
1813 // any match is ok
1814 if ($state == true) {
1815 return true;
1816 }
1817 }
1818 return false;
1819 }
1820 return true;
1821 }
1822
1833 function _check_uri_condition($uri, $condition)
1834 {
1835 // not really implemented here,
1836 // implementations must override
1837 return true;
1838 }
1839
1840
1847 function _check_lock_status($path, $exclusive_only = false)
1848 {
1849 // FIXME depth -> ignored for now
1850 if (method_exists($this, "checkLock")) {
1851 // is locked?
1852 $lock = $this->checkLock($path);
1853
1854 // ... and lock is not owned?
1855 if (is_array($lock) && count($lock)) {
1856 // FIXME doesn't check uri restrictions yet
1857 if (!strstr($_SERVER["HTTP_IF"], $lock["token"])) {
1858 if (!$exclusive_only || ($lock["scope"] !== "shared"))
1859 return false;
1860 }
1861 }
1862 }
1863 return true;
1864 }
1865
1866
1867 // }}}
1868
1869
1877 {
1878 // no lock support without checklock() method
1879 if (!method_exists($this, "checklock")) {
1880 return "";
1881 }
1882
1883 // collect response here
1884 $activelocks = "";
1885
1886 // get checklock() reply
1887 $lock = $this->checklock($path);
1888
1889 // generate <activelock> block for returned data
1890 if (is_array($lock) && count($lock)) {
1891 // check for 'timeout' or 'expires'
1892 if (!empty($lock["expires"])) {
1893 $timeout = "Second-".($lock["expires"] - time());
1894 } else if (!empty($lock["timeout"])) {
1895 $timeout = "Second-$lock[timeout]";
1896 } else {
1897 $timeout = "Infinite";
1898 }
1899
1900 // genreate response block
1901 $activelocks.= "
1902 <D:activelock>
1903 <D:lockscope><D:$lock[scope]/></D:lockscope>
1904 <D:locktype><D:$lock[type]/></D:locktype>
1905 <D:depth>$lock[depth]</D:depth>
1906 <D:owner>$lock[owner]</D:owner>
1907 <D:timeout>$timeout</D:timeout>
1908 <D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
1909 </D:activelock>
1910 ";
1911 }
1912 //$this->writelog('lockdiscovery('.$path.'):'.$activeclocks);
1913
1914 // return generated response
1915 return $activelocks;
1916 }
1917
1924 function http_status($status)
1925 {
1926 // simplified success case
1927 if($status === true) {
1928 $status = "200 OK";
1929 }
1930 //$this->writelog('http_status('.$status.')');
1931
1932 // remember status
1933 $this->_http_status = $status;
1934
1935 // generate HTTP status response
1936 header("HTTP/1.1 $status");
1937 header("X-WebDAV-Status: $status", true);
1938 }
1939
1949 function _urlencode($url)
1950 {
1951 return strtr($url, array(" "=>"%20",
1952 "&"=>"%26",
1953 "<"=>"%3C",
1954 ">"=>"%3E",
1955 ));
1956 }
1957
1967 {
1968 // BEGIN WebDAV
1969 // urldecode wrongly replaces '+' characters by ' ' characters.
1970 // We replace '+' into '%2b' before passing the path through urldecode.
1971 //return urldecode($path);
1972 $result =& urldecode(str_replace('+','%2b',$path));
1973 //$this->writelog('_urldecode('.$path.'):'.$result);
1974 return $result;
1975 // END PATCH WebDAV
1976 }
1977
1985 {
1986 switch (strtolower($this->_prop_encoding)) {
1987 case "utf-8":
1988 return $text;
1989 case "iso-8859-1":
1990 case "iso-8859-15":
1991 case "latin-1":
1992 default:
1993 return utf8_encode($text);
1994 }
1995 }
1996
2003 function _slashify($path) {
2004 if ($path[strlen($path)-1] != '/') {
2005 $path = $path."/";
2006 }
2007 return $path;
2008 }
2009// BEGIN WebDAV
2016 private function writelog($message)
2017 {
2018 global $log, $ilUser;
2019
2020 $log->write(
2021 $ilUser->getLogin()
2022 .' DAV Server.'.str_replace("\n",";",$message)
2023 );
2024 /*
2025 if ($this->logFile)
2026 {
2027 $fh = fopen($this->logFile, 'a');
2028 fwrite($fh, date('Y-m-d h:i:s '));
2029 fwrite($fh, str_replace("\n",";",$message));
2030 fwrite($fh, "\n\n");
2031 fclose($fh);
2032 }*/
2033 }
2034// END PATCH WebDAV
2035}
2036
2037 /*
2038 * Local variables:
2039 * tab-width: 4
2040 * c-basic-offset: 4
2041 * End:
2042 */
2043?>
$result
print $file
$n
Definition: RandomTest.php:80
$size
Definition: RandomTest.php:79
_new_uuid()
generate Unique Universal IDentifier for lock token
Definition: Server.php:1601
http_MOVE()
MOVE method handler.
Definition: Server.php:1284
writelog($message)
Writes a message to the logfile.,.
Definition: Server.php:2016
http_MKCOL()
MKCOL method handler.
Definition: Server.php:830
http_COPY()
COPY method handler.
Definition: Server.php:1267
http_OPTIONS()
GET implementation.
Definition: Server.php:478
_new_locktoken()
create a new opaque lock token as defined in RFC2518
Definition: Server.php:1631
http_GET()
GET method handler.
Definition: Server.php:851
_check_lock_status($path, $exclusive_only=false)
Definition: Server.php:1847
http_PUT()
PUT method handler.
Definition: Server.php:1089
http_LOCK()
LOCK method handler.
Definition: Server.php:1307
_if_header_lexer($string, &$pos)
Definition: Server.php:1647
_check_if_header_conditions()
check if conditions from "If:" headers are meat
Definition: Server.php:1786
_multipart_byterange_header($mimetype=false, $from=false, $to=false, $total=false)
generate separator headers for multipart response
Definition: Server.php:1020
_urlencode($url)
private minimalistic version of PHP urlencode()
Definition: Server.php:1949
_check_uri_condition($uri, $condition)
Check a single URI condition parsed from an if-header.
Definition: Server.php:1833
_prop_encode($text)
UTF-8 encode property values if not already done so.
Definition: Server.php:1984
_if_header_parser($str)
parse If: header
Definition: Server.php:1701
http_PROPPATCH()
PROPPATCH method handler.
Definition: Server.php:773
serveRequest()
Serve WebDAV HTTP request.
Definition: Server.php:125
http_DELETE()
DELETE method handler.
Definition: Server.php:1232
HTTP_WebDAV_Server()
Constructor.
Definition: Server.php:108
http_UNLOCK()
UNLOCK method handler.
Definition: Server.php:1422
_check_auth()
check authentication if check is implemented
Definition: Server.php:1573
http_HEAD()
HEAD method handler.
Definition: Server.php:1059
_get_ranges(&$options)
parse HTTP Range: header
Definition: Server.php:986
lockdiscovery($path)
Generate lockdiscovery reply from checklock() result.
Definition: Server.php:1876
http_PROPFIND()
PROPFIND method handler.
Definition: Server.php:512
_urldecode($path)
private version of PHP urldecode
Definition: Server.php:1966
mkprop()
helper for property element creation
Definition: Server.php:1551
_slashify($path)
Slashify - make sure path ends in a slash.
Definition: Server.php:2003
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1924
_allow()
check for implemented HTTP methods
Definition: Server.php:1511
$text
exit
Definition: login.php:54
Virtual base class for implementing WebDAV servers.
$url
Definition: shib_logout.php:72
if(!is_array($argv)) $options
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
global $ilUser
Definition: imgupload.php:15