ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 
23 require_once "Services/WebDAV/classes/Tools/_parse_propfind.php";
24 require_once "Services/WebDAV/classes/Tools/_parse_proppatch.php";
25 require_once "Services/WebDAV/classes/Tools/_parse_lockinfo.php";
26 
27 
38 {
39  // {{{ Member Variables
40 
46  var $uri;
47 
48 
54  var $base_uri;
55 
56 
62  var $path;
63 
69  var $http_auth_realm = "PHP WebDAV";
70 
76  var $dav_powered_by = "";
77 
84 
90  var $_http_status = "200 OK";
91 
97  var $_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  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']) && $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 
986  function _get_ranges(&$options)
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 (preg_match("/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 (!preg_match("/^<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 
1876  function lockdiscovery($path)
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 
1966  function _urldecode($path)
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 
1984  function _prop_encode($text)
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 $DIC;
2019  $log = $DIC['log'];
2020  $ilUser = $DIC['ilUser'];
2021 
2022  $log->write(
2023  $ilUser->getLogin()
2024  .' DAV Server.'.str_replace("\n",";",$message)
2025  );
2026  }
2027 // END PATCH WebDAV
2028 }
2029 ?>
$files
Definition: add-vimline.php:18
http_OPTIONS()
GET implementation.
Definition: Server.php:478
http_PROPPATCH()
PROPPATCH method handler.
Definition: Server.php:773
_get_ranges(&$options)
parse HTTP Range: header
Definition: Server.php:986
$size
Definition: RandomTest.php:79
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
_allow()
check for implemented HTTP methods
Definition: Server.php:1511
__construct()
Constructor.
Definition: Server.php:108
_if_header_lexer($string, &$pos)
Definition: Server.php:1647
$result
http_UNLOCK()
UNLOCK method handler.
Definition: Server.php:1422
_if_header_parser($str)
parse If: header
Definition: Server.php:1701
http_PUT()
PUT method handler.
Definition: Server.php:1089
_multipart_byterange_header($mimetype=false, $from=false, $to=false, $total=false)
generate separator headers for multipart response
Definition: Server.php:1020
_check_if_header_conditions()
check if conditions from "If:" headers are meat
Definition: Server.php:1786
_urldecode($path)
private version of PHP urldecode
Definition: Server.php:1966
_urlencode($url)
private minimalistic version of PHP urlencode()
Definition: Server.php:1949
http_HEAD()
HEAD method handler.
Definition: Server.php:1059
http_GET()
GET method handler.
Definition: Server.php:851
http_MOVE()
MOVE method handler.
Definition: Server.php:1284
http_DELETE()
DELETE method handler.
Definition: Server.php:1232
http_PROPFIND()
PROPFIND method handler.
Definition: Server.php:512
Virtual base class for implementing WebDAV servers.
Definition: Server.php:23
$url
Definition: shib_logout.php:72
$total
Definition: Utf8Test.php:87
http_COPY()
COPY method handler.
Definition: Server.php:1267
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1924
_new_locktoken()
create a new opaque lock token as defined in RFC2518
Definition: Server.php:1631
_check_lock_status($path, $exclusive_only=false)
Definition: Server.php:1847
serveRequest()
Serve WebDAV HTTP request.
Definition: Server.php:125
if(!is_array($argv)) $options
mkprop()
helper for property element creation
Definition: Server.php:1551
$ilUser
Definition: imgupload.php:18
Add a drawing to the header
Definition: 04printing.php:69
_check_auth()
check authentication if check is implemented
Definition: Server.php:1573
_check_uri_condition($uri, $condition)
Check a single URI condition parsed from an if-header.
Definition: Server.php:1833
$n
Definition: RandomTest.php:80
_slashify($path)
Slashify - make sure path ends in a slash.
Definition: Server.php:2003
Create styles array
The data for the language used.
http_MKCOL()
MKCOL method handler.
Definition: Server.php:830
$text
lockdiscovery($path)
Generate lockdiscovery reply from checklock() result.
Definition: Server.php:1876
global $DIC
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
_new_uuid()
generate Unique Universal IDentifier for lock token
Definition: Server.php:1601
_prop_encode($text)
UTF-8 encode property values if not already done so.
Definition: Server.php:1984
http_LOCK()
LOCK method handler.
Definition: Server.php:1307
writelog($message)
Writes a message to the logfile.,.
Definition: Server.php:2016