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";
129 $uri = (@
$_SERVER[
"HTTPS"] ===
"on" ?
"https:" :
"http:");
130 $uri.=
"//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
132 $this->base_uri =
$uri;
133 $this->uri = $uri .
$_SERVER[PATH_INFO];
136 if (empty($this->dav_powered_by)) {
137 header(
"X-Dav-Powered-By: PHP class: ".get_class($this));
139 header(
"X-Dav-Powered-By: ".$this->dav_powered_by );
142 $this->
writelog(__METHOD__.
': Using uri: '.$this->uri);
150 header(
'WWW-Authenticate: Basic realm="'.($this->http_auth_realm).
'"');
155 $this->
writelog(
'Check auth failed');
162 $this->
writelog(__METHOD__.
': Precondition failed.');
169 if (!strlen($this->path)) {
170 header(
"Location: ".$this->base_uri.
"/");
171 $this->
writelog(
'HTTP_WebDAV_Server.ServeRequest() missing path info');
184 $method = strtolower(
$_SERVER[
"REQUEST_METHOD"]);
185 $wrapper =
"http_".$method;
187 $this->
writelog(__METHOD__.
': Using request method: '.$method);
190 if ($method ==
"head" && !method_exists($this,
"head"))
193 $this->
writelog(__METHOD__.
': Using head emulation by get.');
196 if (method_exists($this, $wrapper) && ($method ==
"options" || method_exists($this, $method)))
198 $this->
writelog(__METHOD__.
': Calling wrapper: '.$wrapper);
203 if (
$_SERVER[
"REQUEST_METHOD"] ==
"LOCK")
205 $this->
writelog(__METHOD__.
': Method not found/implemented. Sending 412');
210 $this->
writelog(__METHOD__.
': Method not found/implemented. Sending allowd methods');
482 header(
"MS-Author-Via: DAV");
489 if (isset($allow[
'LOCK'])) {
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");
518 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
526 if (!$propinfo->success) {
530 $options[
'props'] = $propinfo->props;
543 $ns_defs =
"xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
549 if (!isset(
$file[
"props"]) || !is_array(
$file[
"props"])) {
554 foreach(
$file[
"props"] as $key => $prop) {
567 unset(
$files[
"files"][$filekey][
"props"][$key][
"val"]);
575 if ( $reqprop[
"name"] == $prop[
"name"]
576 && $reqprop[
"xmlns"] == $prop[
"ns"]) {
584 $files[
"files"][$filekey][
"props"][$key]=
"";
591 if (empty($prop[
"ns"]))
continue;
593 if ($ns ==
"DAV:")
continue;
594 if (isset($ns_hash[$ns]))
continue;
597 $ns_name =
"ns".(count($ns_hash) + 1);
598 $ns_hash[$ns] = $ns_name;
599 $ns_defs .=
" xmlns:$ns_name=\"$ns\"";
605 foreach(
$options[
"props"] as $reqprop) {
606 if($reqprop[
'name']==
"")
continue;
611 foreach(
$file[
"props"] as $prop) {
612 if ( $reqprop[
"name"] == $prop[
"name"]
613 && $reqprop[
"xmlns"] == $prop[
"ns"]) {
620 if($reqprop[
"xmlns"]===
"DAV:" && $reqprop[
"name"]===
"lockdiscovery") {
622 $files[
"files"][$filekey][
"props"][]
628 $files[
"files"][$filekey][
"noprops"][] =
629 $this->
mkprop($reqprop[
"xmlns"], $reqprop[
"name"],
"");
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]\"";
645 header(
'Content-Type: text/xml; charset="utf-8"');
648 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
649 echo "<D:multistatus xmlns:D=\"DAV:\">\n";
653 if(!is_array($file) || empty($file) || !isset($file[
"path"]))
continue;
654 $path = $file[
'path'];
655 if(!is_string($path) || $path===
"")
continue;
657 echo " <D:response $ns_defs>\n";
664 echo " <D:href>$href</D:href>\n";
667 if (isset($file[
"props"]) && is_array($file[
"props"])) {
668 echo " <D:propstat>\n";
671 foreach($file[
"props"] as $key => $prop) {
673 if (!is_array($prop))
continue;
674 if (!isset($prop[
"name"]))
continue;
675 if (!isset($prop[
"val"]) || $prop[
"val"] ===
"" || $prop[
"val"] ===
false) {
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";
682 echo " <$prop[name] xmlns=\"\"/>";
684 }
else if ($prop[
"ns"] ==
"DAV:") {
686 switch ($prop[
"name"]) {
688 echo " <D:creationdate ns0:dt=\"dateTime.tz\">" 690 . gmdate(
"Y-m-d\\TH:i:s\\Z",$prop[
'val'])
693 .
"</D:creationdate>\n";
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";
701 echo " <D:resourcetype><D:$prop[val]/></D:resourcetype>\n";
703 case "supportedlock":
704 echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
706 case "lockdiscovery":
707 echo " <D:lockdiscovery>\n";
709 echo " </D:lockdiscovery>\n";
712 echo " <D:$prop[name]>" 714 .
"</D:$prop[name]>\n";
720 echo " <" . $ns_hash[$prop[
"ns"]] .
":$prop[name]>" 722 .
"</" . $ns_hash[$prop[
"ns"]] .
":$prop[name]>\n";
724 echo " <$prop[name] xmlns=\"\">" 726 .
"</$prop[name]>\n";
732 echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
733 echo " </D:propstat>\n";
737 if (isset($file[
"noprops"])) {
738 echo " <D:propstat>\n";
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";
747 echo " <" . $ns_hash[$prop[
"ns"]] .
":$prop[name]/>\n";
752 echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
753 echo " </D:propstat>\n";
756 echo " </D:response>\n";
759 echo "</D:multistatus>\n";
781 if (!$propinfo->success) {
786 $options[
'props'] = $propinfo->props;
788 $responsedescr = $this->proppatch(
$options);
791 header(
'Content-Type: text/xml; charset="utf-8"');
793 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
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";
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";
806 if ($responsedescr) {
807 echo " <D:responsedescription>".
809 "</D:responsedescription>\n";
812 echo " </D:response>\n";
813 echo "</D:multistatus>\n";
859 if (
true === ($status = $this->
get(
$options))) {
860 if (!headers_sent()) {
864 $options[
'mimetype'] =
"application/octet-stream";
866 header(
"Content-type: $options[mimetype]");
869 header(
"Last-modified:".gmdate(
"D, d M Y H:i:s ",
$options[
'mtime']).
"GMT");
874 if (!empty(
$options[
'ranges']) && (0===fseek(
$options[
'stream'], 0, SEEK_SET))) {
877 if (count(
$options[
'ranges']) === 1) {
880 if (isset($range[
'start'])) {
881 fseek(
$options[
'stream'], $range[
'start'], SEEK_SET);
883 $this->
http_status(
"416 Requested range not satisfiable");
887 if (isset($range[
'end']) && $range[
'end'] !=
'') {
888 $size = $range[
'end']-$range[
'start']+1;
890 header(
"Content-length: $size");
891 header(
"Content-range: $range[start]-$range[end]/" 893 while (
$size && !feof($options[
'stream'])) {
894 $buffer = fread($options[
'stream'], 4096);
895 $size -= strlen($buffer);
902 header(
"Content-range: $start-$end/" 908 header(
"Content-length: ".$range[
'last']);
909 fseek(
$options[
'stream'], -$range[
'last'], SEEK_END);
914 foreach (
$options[
'ranges'] as $range) {
916 if (isset($range[
'start'])) {
917 $from = $range[
'start'];
918 $to = !empty($range[
'end']) ? $range[
'end'] :
$options[
'size']-1;
920 $from =
$options[
'size'] - $range[
'last']-1;
924 $size = $to - $from + 1;
930 $buffer = fread(
$options[
'stream'], 4096);
931 $size -= strlen($buffer);
947 while (! feof(
$options[
'stream'])) {
948 $buffer = fread(
$options[
'stream'], 4096);
955 } elseif (isset(
$options[
'data'])) {
966 if (
false === $status) {
968 $status =
'404 Not Found';
973 if (!headers_sent()) {
989 if (isset(
$_SERVER[
'HTTP_RANGE'])) {
992 if (preg_match(
"/bytes[[:space:]]*=[[:space:]]*(.+)/",
$_SERVER[
'HTTP_RANGE'], $matches)) {
996 foreach (explode(
",", $matches[1]) as $range) {
998 list(
$start, $end) = explode(
"-", $range);
1000 ?
array(
"last"=>$end)
1022 if ($mimetype ===
false) {
1023 if (!isset($this->multipart_separator)) {
1028 $this->multipart_separator =
"SEPARATOR_".md5(microtime());
1031 header(
"Content-type: multipart/byteranges; boundary=".$this->multipart_separator);
1036 echo "\n--{$this->multipart_separator}--";
1040 echo "\n--{$this->multipart_separator}\n";
1041 echo "Content-type: $mimetype\n";
1065 if (method_exists($this,
"HEAD")) {
1067 }
else if (method_exists($this,
"GET")) {
1073 if($status===
true) $status =
"200 OK";
1074 if($status===
false) $status =
"404 Not found";
1097 if (isset(
$_SERVER[
"CONTENT_TYPE"])) {
1099 if (!strncmp(
$_SERVER[
"CONTENT_TYPE"],
"multipart/", 10)) {
1101 echo "The service does not support mulipart PUT requests";
1107 $options[
"content_type"] =
"application/octet-stream";
1115 foreach (
$_SERVER as $key => $val) {
1116 if (strncmp($key,
"HTTP_CONTENT", 11))
continue;
1118 case 'HTTP_CONTENT_ENCODING':
1121 echo "The service does not support '$val' content encoding";
1124 case 'HTTP_CONTENT_LANGUAGE':
1127 $options[
"content_language"] = $value;
1130 case 'HTTP_CONTENT_LOCATION':
1136 case 'HTTP_CONTENT_RANGE':
1140 if (!preg_match(
'@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1142 echo "The service does only support single byte ranges";
1146 $range =
array(
"start"=>$matches[1],
"end"=>$matches[2]);
1147 if (is_numeric($matches[3])) {
1148 $range[
"total_length"] = $matches[3];
1150 $option[
"ranges"][] = $range;
1157 case 'HTTP_CONTENT_MD5':
1160 echo "The service does not support content MD5 checksum verification";
1163 case 'HTTP_CONTENT_LENGTH':
1170 echo "The service does not support '$key'";
1175 $options[
"stream"] = fopen(
"php://input",
"r");
1179 if ($stat ==
false) {
1180 $stat =
"403 Forbidden";
1181 }
else if (is_resource($stat) && get_resource_type($stat) ==
"stream") {
1184 $stat =
$options[
"new"] ?
"201 Created" :
"204 No Content";
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";
1194 $stat =
"403 Forbidden";
1197 while (!feof(
$options[
"stream"])) {
1199 if (
false === ($written = fwrite($stream, fread(
$options[
"stream"], 4096)))) {
1201 $stat =
"403 Forbidden";
1235 if (isset(
$_SERVER[
"HTTP_DEPTH"])) {
1236 if (
$_SERVER[
"HTTP_DEPTH"] !=
"infinity") {
1312 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
1318 if (isset(
$_SERVER[
"HTTP_TIMEOUT"])) {
1335 if (!$lockinfo->success) {
1346 $options[
"scope"] = $lockinfo->lockscope;
1347 $options[
"type"] = $lockinfo->locktype;
1348 $options[
"owner"] = $lockinfo->owner;
1355 if(is_bool($stat)) {
1356 $http_stat = $stat ?
"200 OK" :
"423 Locked";
1363 if ($http_stat{0} == 2) {
1368 $timeout =
"Second-".($options[
'timeout']-
time());
1370 $timeout =
"Second-$options[timeout]";
1373 $timeout =
"Infinite";
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";
1427 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
1454 if (isset(
$_SERVER[
"HTTP_DEPTH"])) {
1460 extract(parse_url(
$_SERVER[
"HTTP_DESTINATION"]));
1466 if (isset($port) && $port != 80)
1467 $http_host.=
":$port";
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;
1474 if ($http_host == $http_header_host &&
1475 !strncmp(
$_SERVER[
"SCRIPT_NAME"], $path,
1476 strlen(
$_SERVER[
"SCRIPT_NAME"]))) {
1491 if (isset(
$_SERVER[
"HTTP_OVERWRITE"])) {
1514 $allow =
array(
"OPTIONS" =>
"OPTIONS");
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;
1529 if (isset($allow[
"GET"]))
1530 $allow[
"HEAD"] =
"HEAD";
1533 if (!method_exists($this,
"checklock")) {
1534 unset($allow[
"LOCK"]);
1535 unset($allow[
"UNLOCK"]);
1553 $args = func_get_args();
1554 if (count($args) == 3) {
1555 return array(
"ns" => $args[0],
1559 return array(
"ns" =>
"DAV:",
1575 if (method_exists($this,
"checkAuth")) {
1577 return $this->checkAuth(@
$_SERVER[
"AUTH_TYPE"],
1580 }
else if (method_exists($this,
"check_auth")) {
1582 return $this->check_auth(@
$_SERVER[
"AUTH_TYPE"],
1604 if (function_exists(
"uuid_create")) {
1605 return uuid_create();
1609 $uuid = md5(microtime().getmypid());
1613 $n = 8 + (ord($uuid{16}) & 3);
1614 $hex =
"0123456789abcdef";
1615 $uuid{16} = $hex{
$n};
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);
1633 return "opaquelocktoken:".$this->_new_uuid();
1650 while (ctype_space($string{$pos})) {
1655 if (strlen($string) <= $pos) {
1660 $c = $string{$pos++};
1666 $pos2 = strpos($string,
">", $pos);
1667 $uri = substr($string, $pos, $pos2 - $pos);
1669 return array(
"URI", $uri);
1673 if ($string{$pos} ==
"W") {
1674 $type =
"ETAG_WEAK";
1677 $type =
"ETAG_STRONG";
1679 $pos2 = strpos($string,
"]", $pos);
1680 $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
1682 return array($type, $etag);
1687 return array(
"NOT",
"Not");
1691 return array(
"CHAR", $c);
1704 $len = strlen($str);
1709 while ($pos < $len) {
1714 if ($token[0] ==
"URI") {
1722 if ($token[0] !=
"CHAR" || $token[1] !=
"(") {
1731 if ($token[0] ==
"NOT") {
1735 switch ($token[0]) {
1737 switch ($token[1]) {
1750 $list[] = $not.
"<$token[1]>";
1754 $list[] = $not.
"[W/'$token[1]']>";
1758 $list[] = $not.
"['$token[1]']>";
1767 if (@is_array($uris[$uri])) {
1768 $uris[
$uri] = array_merge($uris[$uri],$list);
1770 $uris[
$uri] = $list;
1789 $this->_if_header_uris =
1792 foreach($this->_if_header_uris as $uri => $conditions) {
1798 foreach($conditions as $condition) {
1802 if (!strncmp($condition,
"<opaquelocktoken:", strlen(
"<opaquelocktoken"))) {
1803 if (!preg_match(
"/^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$/", $condition)) {
1814 if ($state ==
true) {
1850 if (method_exists($this,
"checkLock")) {
1852 $lock = $this->checkLock($path);
1855 if (is_array($lock) && count($lock)) {
1857 if (!strstr(
$_SERVER[
"HTTP_IF"], $lock[
"token"])) {
1858 if (!$exclusive_only || ($lock[
"scope"] !==
"shared"))
1879 if (!method_exists($this,
"checklock")) {
1887 $lock = $this->checklock($path);
1890 if (is_array($lock) && count($lock)) {
1892 if (!empty($lock[
"expires"])) {
1893 $timeout =
"Second-".($lock[
"expires"] -
time());
1894 }
else if (!empty($lock[
"timeout"])) {
1895 $timeout =
"Second-$lock[timeout]";
1897 $timeout =
"Infinite";
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> 1915 return $activelocks;
1927 if($status ===
true) {
1933 $this->_http_status = $status;
1936 header(
"HTTP/1.1 $status");
1937 header(
"X-WebDAV-Status: $status",
true);
1972 $result =& urldecode(str_replace(
'+',
'%2b',$path));
1986 switch (strtolower($this->_prop_encoding)) {
1993 return utf8_encode(
$text);
2004 if ($path[strlen($path)-1] !=
'/') {
2024 .
' DAV Server.'.str_replace(
"\n",
";",$message)
http_OPTIONS()
GET implementation.
http_PROPPATCH()
PROPPATCH method handler.
_get_ranges(&$options)
parse HTTP Range: header
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
_allow()
check for implemented HTTP methods
__construct()
Constructor.
_if_header_lexer($string, &$pos)
http_UNLOCK()
UNLOCK method handler.
_if_header_parser($str)
parse If: header
http_PUT()
PUT method handler.
_multipart_byterange_header($mimetype=false, $from=false, $to=false, $total=false)
generate separator headers for multipart response
_check_if_header_conditions()
check if conditions from "If:" headers are meat
_urldecode($path)
private version of PHP urldecode
_urlencode($url)
private minimalistic version of PHP urlencode()
http_HEAD()
HEAD method handler.
http_GET()
GET method handler.
http_MOVE()
MOVE method handler.
http_DELETE()
DELETE method handler.
http_PROPFIND()
PROPFIND method handler.
Virtual base class for implementing WebDAV servers.
http_COPY()
COPY method handler.
http_status($status)
set HTTP return status and mirror it in a private header
_new_locktoken()
create a new opaque lock token as defined in RFC2518
_check_lock_status($path, $exclusive_only=false)
serveRequest()
Serve WebDAV HTTP request.
if(!is_array($argv)) $options
mkprop()
helper for property element creation
Add a drawing to the header
_check_auth()
check authentication if check is implemented
_check_uri_condition($uri, $condition)
Check a single URI condition parsed from an if-header.
_slashify($path)
Slashify - make sure path ends in a slash.
Create styles array
The data for the language used.
http_MKCOL()
MKCOL method handler.
lockdiscovery($path)
Generate lockdiscovery reply from checklock() result.
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
_prop_encode($text)
UTF-8 encode property values if not already done so.
http_LOCK()
LOCK method handler.
writelog($message)
Writes a message to the logfile.,.