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;
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")) {
192 $this->
writelog(__METHOD__ .
': Using head emulation by get.');
195 if (method_exists($this, $wrapper) && ($method ==
"options" || method_exists($this, $method))) {
196 $this->
writelog(__METHOD__ .
': Calling wrapper: ' . $wrapper);
199 if (
$_SERVER[
"REQUEST_METHOD"] ==
"LOCK") {
200 $this->
writelog(__METHOD__ .
': Method not found/implemented. Sending 412');
203 $this->
writelog(__METHOD__ .
': Method not found/implemented. Sending allowd methods');
475 header(
"MS-Author-Via: DAV");
482 if (isset($allow[
'LOCK'])) {
488 header(
"DAV: " . join(
",", $dav));
489 header(
"Allow: " . join(
", ", $allow));
490 $this->
writelog(__METHOD__ .
': dav=' . var_export($dav,
true) .
' allow=' . var_export($allow,
true));
491 header(
"Content-length: 0");
511 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
519 if (!$propinfo->success) {
523 $options[
'props'] = $propinfo->props;
536 $ns_defs =
"xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
542 if (!isset(
$file[
"props"]) || !is_array(
$file[
"props"])) {
547 foreach (
$file[
"props"] as
$key => $prop) {
560 unset(
$files[
"files"][$filekey][
"props"][
$key][
"val"]);
568 if ($reqprop[
"name"] == $prop[
"name"]
569 && $reqprop[
"xmlns"] == $prop[
"ns"]) {
584 if (empty($prop[
"ns"])) {
591 if (isset($ns_hash[$ns])) {
596 $ns_name =
"ns" . (count($ns_hash) + 1);
597 $ns_hash[$ns] = $ns_name;
598 $ns_defs .=
" xmlns:$ns_name=\"$ns\"";
604 foreach (
$options[
"props"] as $reqprop) {
605 if ($reqprop[
'name']==
"") {
612 foreach (
$file[
"props"] as $prop) {
613 if ($reqprop[
"name"] == $prop[
"name"]
614 && $reqprop[
"xmlns"] == $prop[
"ns"]) {
621 if ($reqprop[
"xmlns"]===
"DAV:" && $reqprop[
"name"]===
"lockdiscovery") {
623 $files[
"files"][$filekey][
"props"][]
631 $files[
"files"][$filekey][
"noprops"][] =
632 $this->
mkprop($reqprop[
"xmlns"], $reqprop[
"name"],
"");
635 if ($reqprop[
"xmlns"] !=
"DAV:" && !isset($ns_hash[$reqprop[
"xmlns"]])) {
636 $ns_name =
"ns" . (count($ns_hash) + 1);
637 $ns_hash[$reqprop[
"xmlns"]] = $ns_name;
638 $ns_defs .=
" xmlns:$ns_name=\"$reqprop[xmlns]\"";
648 header(
'Content-Type: text/xml; charset="utf-8"');
651 echo
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
652 echo
"<D:multistatus xmlns:D=\"DAV:\">\n";
656 if (!is_array($file) || empty($file) || !isset($file[
"path"])) {
659 $path = $file[
'path'];
664 echo
" <D:response $ns_defs>\n";
671 echo
" <D:href>$href</D:href>\n";
674 if (isset($file[
"props"]) && is_array($file[
"props"])) {
675 echo
" <D:propstat>\n";
678 foreach ($file[
"props"] as
$key => $prop) {
679 if (!is_array($prop)) {
682 if (!isset($prop[
"name"])) {
685 if (!isset($prop[
"val"]) || $prop[
"val"] ===
"" || $prop[
"val"] ===
false) {
687 if ($prop[
"ns"]==
"DAV:") {
688 echo
" <D:$prop[name]/>\n";
689 } elseif (!empty($prop[
"ns"])) {
690 echo
" <" . $ns_hash[$prop[
"ns"]] .
":$prop[name]/>\n";
692 echo
" <$prop[name] xmlns=\"\"/>";
694 } elseif ($prop[
"ns"] ==
"DAV:") {
696 switch ($prop[
"name"]) {
698 echo
" <D:creationdate ns0:dt=\"dateTime.tz\">" 700 . gmdate(
"Y-m-d\\TH:i:s\\Z", $prop[
'val'])
703 .
"</D:creationdate>\n";
705 case "getlastmodified":
706 echo
" <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">" 707 . gmdate(
"D, d M Y H:i:s ", $prop[
'val'])
708 .
"GMT</D:getlastmodified>\n";
711 echo
" <D:resourcetype><D:$prop[val]/></D:resourcetype>\n";
713 case "supportedlock":
714 echo
" <D:supportedlock>$prop[val]</D:supportedlock>\n";
716 case "lockdiscovery":
717 echo
" <D:lockdiscovery>\n";
719 echo
" </D:lockdiscovery>\n";
722 echo
" <D:$prop[name]>" 724 .
"</D:$prop[name]>\n";
730 echo
" <" . $ns_hash[$prop[
"ns"]] .
":$prop[name]>" 732 .
"</" . $ns_hash[$prop[
"ns"]] .
":$prop[name]>\n";
734 echo
" <$prop[name] xmlns=\"\">" 736 .
"</$prop[name]>\n";
742 echo
" <D:status>HTTP/1.1 200 OK</D:status>\n";
743 echo
" </D:propstat>\n";
747 if (isset($file[
"noprops"])) {
748 echo
" <D:propstat>\n";
751 foreach ($file[
"noprops"] as
$key => $prop) {
752 if ($prop[
"ns"] ==
"DAV:") {
753 echo
" <D:$prop[name]/>\n";
754 } elseif ($prop[
"ns"] ==
"") {
755 echo
" <$prop[name] xmlns=\"\"/>\n";
757 echo
" <" . $ns_hash[$prop[
"ns"]] .
":$prop[name]/>\n";
762 echo
" <D:status>HTTP/1.1 404 Not Found</D:status>\n";
763 echo
" </D:propstat>\n";
766 echo
" </D:response>\n";
769 echo
"</D:multistatus>\n";
791 if (!$propinfo->success) {
796 $options[
'props'] = $propinfo->props;
798 $responsedescr = $this->proppatch(
$options);
801 header(
'Content-Type: text/xml; charset="utf-8"');
803 echo
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
805 echo
"<D:multistatus xmlns:D=\"DAV:\">\n";
806 echo
" <D:response>\n";
807 echo
" <D:href>" . $this->
_urlencode(
$_SERVER[
"SCRIPT_NAME"] . $this->path) .
"</D:href>\n";
809 foreach (
$options[
"props"] as $prop) {
810 echo
" <D:propstat>\n";
811 echo
" <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
812 echo
" <D:status>HTTP/1.1 $prop[status]</D:status>\n";
813 echo
" </D:propstat>\n";
816 if ($responsedescr) {
817 echo
" <D:responsedescription>" .
819 "</D:responsedescription>\n";
822 echo
" </D:response>\n";
823 echo
"</D:multistatus>\n";
869 if (
true === ($status = $this->
get(
$options))) {
870 if (!headers_sent()) {
874 $options[
'mimetype'] =
"application/octet-stream";
876 header(
"Content-type: $options[mimetype]");
879 header(
"Last-modified:" . gmdate(
"D, d M Y H:i:s ",
$options[
'mtime']) .
"GMT");
884 if (!empty(
$options[
'ranges']) && (0===fseek(
$options[
'stream'], 0, SEEK_SET))) {
887 if (count(
$options[
'ranges']) === 1) {
890 if (isset($range[
'start'])) {
891 fseek(
$options[
'stream'], $range[
'start'], SEEK_SET);
893 $this->
http_status(
"416 Requested range not satisfiable");
897 if (isset($range[
'end']) && $range[
'end'] !=
'') {
898 $size = $range[
'end']-$range[
'start']+1;
900 header(
"Content-length: $size");
901 header(
"Content-range: $range[start]-$range[end]/" 903 while (
$size && !feof($options[
'stream'])) {
904 $buffer = fread($options[
'stream'], 4096);
905 $size -= strlen($buffer);
911 header(
"Content-length: " . (
$options[
'size'] - $range[
'start']));
912 header(
"Content-range: $start-$end/" 918 header(
"Content-length: " . $range[
'last']);
919 fseek(
$options[
'stream'], -$range[
'last'], SEEK_END);
924 foreach (
$options[
'ranges'] as $range) {
926 if (isset($range[
'start'])) {
927 $from = $range[
'start'];
928 $to = !empty($range[
'end']) ? $range[
'end'] :
$options[
'size']-1;
938 fseek(
$options[
'stream'], $start, SEEK_SET);
940 $buffer = fread(
$options[
'stream'], 4096);
941 $size -= strlen($buffer);
958 $buffer = fread(
$options[
'stream'], 4096);
965 } elseif (isset(
$options[
'data'])) {
976 if (
false === $status) {
978 $status =
'404 Not Found';
983 if (!headers_sent()) {
999 if (isset(
$_SERVER[
'HTTP_RANGE'])) {
1002 if (preg_match(
"/bytes[[:space:]]*=[[:space:]]*(.+)/",
$_SERVER[
'HTTP_RANGE'], $matches)) {
1006 foreach (explode(
",", $matches[1]) as $range) {
1008 list($start,
$end) = explode(
"-", $range);
1009 $options[
"ranges"][] = ($start===
"")
1032 if ($mimetype ===
false) {
1033 if (!isset($this->multipart_separator)) {
1038 $this->multipart_separator =
"SEPARATOR_" . md5(microtime());
1041 header(
"Content-type: multipart/byteranges; boundary=" . $this->multipart_separator);
1046 echo
"\n--{$this->multipart_separator}--";
1050 echo
"\n--{$this->multipart_separator}\n";
1051 echo
"Content-type: $mimetype\n";
1052 echo
"Content-range: $from-$to/" . (
$total ===
false ?
"*" :
$total);
1075 if (method_exists($this,
"HEAD")) {
1077 } elseif (method_exists($this,
"GET")) {
1083 if ($status===
true) {
1086 if ($status===
false) {
1087 $status =
"404 Not found";
1111 if (isset(
$_SERVER[
"CONTENT_TYPE"])) {
1113 if (!strncmp(
$_SERVER[
"CONTENT_TYPE"],
"multipart/", 10)) {
1115 echo
"The service does not support mulipart PUT requests";
1121 $options[
"content_type"] =
"application/octet-stream";
1130 if (strncmp(
$key,
"HTTP_CONTENT", 11)) {
1134 case 'HTTP_CONTENT_ENCODING':
1137 echo
"The service does not support '$val' content encoding";
1140 case 'HTTP_CONTENT_LANGUAGE':
1143 $options[
"content_language"] = $value;
1146 case 'HTTP_CONTENT_LOCATION':
1152 case 'HTTP_CONTENT_RANGE':
1156 if (!preg_match(
'@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1158 echo
"The service does only support single byte ranges";
1162 $range =
array(
"start"=>$matches[1],
"end"=>$matches[2]);
1163 if (is_numeric($matches[3])) {
1164 $range[
"total_length"] = $matches[3];
1166 $option[
"ranges"][] = $range;
1173 case 'HTTP_CONTENT_MD5':
1176 echo
"The service does not support content MD5 checksum verification";
1179 case 'HTTP_CONTENT_LENGTH':
1186 echo
"The service does not support '$key'";
1191 $options[
"stream"] = fopen(
"php://input",
"r");
1195 if ($stat ==
false) {
1196 $stat =
"403 Forbidden";
1197 } elseif (is_resource($stat) && get_resource_type($stat) ==
"stream") {
1200 $stat =
$options[
"new"] ?
"201 Created" :
"204 No Content";
1204 if (0 == fseek(
$stream, $range[0][
"start"], SEEK_SET)) {
1205 $length = $range[0][
"end"]-$range[0][
"start"]+1;
1207 $stat =
"403 Forbidden";
1210 $stat =
"403 Forbidden";
1213 while (!feof(
$options[
"stream"])) {
1215 if (
false === ($written = fwrite(
$stream, fread(
$options[
"stream"], 4096)))) {
1217 $stat =
"403 Forbidden";
1251 if (isset(
$_SERVER[
"HTTP_DEPTH"])) {
1252 if (
$_SERVER[
"HTTP_DEPTH"] !=
"infinity") {
1328 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
1334 if (isset(
$_SERVER[
"HTTP_TIMEOUT"])) {
1351 if (!$lockinfo->success) {
1362 $options[
"scope"] = $lockinfo->lockscope;
1363 $options[
"type"] = $lockinfo->locktype;
1364 $options[
"owner"] = $lockinfo->owner;
1371 if (is_bool($stat)) {
1372 $http_stat = $stat ?
"200 OK" :
"423 Locked";
1379 if ($http_stat{0} == 2) {
1386 $timeout =
"Second-$options[timeout]";
1389 $timeout =
"Infinite";
1409 header(
'Content-Type: text/xml; charset="utf-8"');
1410 header(
"Lock-Token: <$options[locktoken]>");
1411 echo
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1412 echo
"<D:prop xmlns:D=\"DAV:\">\n";
1413 echo
" <D:lockdiscovery>\n";
1414 echo
" <D:activelock>\n";
1415 echo
" <D:lockscope><D:$options[scope]/></D:lockscope>\n";
1416 echo
" <D:locktype><D:$options[type]/></D:locktype>\n";
1417 echo
" <D:depth>$options[depth]</D:depth>\n";
1418 echo
" <D:owner>$options[owner]</D:owner>\n";
1419 echo
" <D:timeout>$timeout</D:timeout>\n";
1420 echo
" <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n";
1421 echo
" </D:activelock>\n";
1422 echo
" </D:lockdiscovery>\n";
1423 echo
"</D:prop>\n\n";
1443 if (isset(
$_SERVER[
'HTTP_DEPTH'])) {
1470 if (isset(
$_SERVER[
"HTTP_DEPTH"])) {
1476 extract(parse_url(
$_SERVER[
"HTTP_DESTINATION"]));
1482 if (isset($port) && $port != 80) {
1483 $http_host.=
":$port";
1486 list($http_header_host, $http_header_port) = explode(
":",
$_SERVER[
"HTTP_HOST"]);
1487 if (isset($http_header_port) && $http_header_port != 80) {
1488 $http_header_host .=
":" . $http_header_port;
1491 if ($http_host == $http_header_host &&
1510 if (isset(
$_SERVER[
"HTTP_OVERWRITE"])) {
1533 $allow =
array(
"OPTIONS" =>
"OPTIONS");
1538 foreach (get_class_methods($this) as $method) {
1539 if (!strncmp(
"http_", $method, 5)) {
1540 $method = strtoupper(substr($method, 5));
1541 if (method_exists($this, $method)) {
1542 $allow[$method] = $method;
1548 if (isset($allow[
"GET"])) {
1549 $allow[
"HEAD"] =
"HEAD";
1553 if (!method_exists($this,
"checklock")) {
1554 unset($allow[
"LOCK"]);
1555 unset($allow[
"UNLOCK"]);
1573 $args = func_get_args();
1574 if (count($args) == 3) {
1575 return array(
"ns" => $args[0],
1579 return array(
"ns" =>
"DAV:",
1595 if (method_exists($this,
"checkAuth")) {
1597 return $this->checkAuth(
1602 } elseif (method_exists($this,
"check_auth")) {
1604 return $this->check_auth(
1628 if (function_exists(
"uuid_create")) {
1629 return uuid_create();
1633 $uuid = md5(microtime() . getmypid());
1637 $n = 8 + (ord($uuid{16}) & 3);
1638 $hex =
"0123456789abcdef";
1639 $uuid{16} = $hex{
$n};
1642 return substr($uuid, 0, 8) .
"-" 1643 . substr($uuid, 8, 4) .
"-" 1644 . substr($uuid, 12, 4) .
"-" 1645 . substr($uuid, 16, 4) .
"-" 1646 . substr($uuid, 20);
1657 return "opaquelocktoken:" . $this->
_new_uuid();
1674 while (ctype_space($string{$pos})) {
1679 if (strlen($string) <= $pos) {
1684 $c = $string{$pos++};
1690 $pos2 = strpos($string,
">", $pos);
1691 $uri = substr($string, $pos, $pos2 - $pos);
1697 if ($string{$pos} ==
"W") {
1698 $type =
"ETAG_WEAK";
1701 $type =
"ETAG_STRONG";
1703 $pos2 = strpos($string,
"]", $pos);
1704 $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
1711 return array(
"NOT",
"Not");
1715 return array(
"CHAR", $c);
1728 $len = strlen($str);
1733 while ($pos < $len) {
1738 if ($token[0] ==
"URI") {
1746 if ($token[0] !=
"CHAR" || $token[1] !=
"(") {
1755 if ($token[0] ==
"NOT") {
1759 switch ($token[0]) {
1761 switch ($token[1]) {
1774 $list[] = $not .
"<$token[1]>";
1778 $list[] = $not .
"[W/'$token[1]']>";
1782 $list[] = $not .
"['$token[1]']>";
1791 if (@is_array($uris[
$uri])) {
1792 $uris[
$uri] = array_merge($uris[$uri],
$list);
1813 $this->_if_header_uris =
1816 foreach ($this->_if_header_uris as
$uri => $conditions) {
1822 foreach ($conditions as $condition) {
1826 if (!strncmp($condition,
"<opaquelocktoken:", strlen(
"<opaquelocktoken"))) {
1827 if (!preg_match(
"/^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$/", $condition)) {
1874 if (method_exists($this,
"checkLock")) {
1876 $lock = $this->checkLock(
$path);
1879 if (is_array($lock) && count($lock)) {
1881 if (!strstr(
$_SERVER[
"HTTP_IF"], $lock[
"token"])) {
1882 if (!$exclusive_only || ($lock[
"scope"] !==
"shared")) {
1904 if (!method_exists($this,
"checklock")) {
1912 $lock = $this->checklock(
$path);
1915 if (is_array($lock) && count($lock)) {
1917 if (!empty($lock[
"expires"])) {
1918 $timeout =
"Second-" . ($lock[
"expires"] -
time());
1919 } elseif (!empty($lock[
"timeout"])) {
1920 $timeout =
"Second-$lock[timeout]";
1922 $timeout =
"Infinite";
1928 <D:lockscope><D:$lock[scope]/></D:lockscope> 1929 <D:locktype><D:$lock[type]/></D:locktype> 1930 <D:depth>$lock[depth]</D:depth> 1931 <D:owner>$lock[owner]</D:owner> 1932 <D:timeout>$timeout</D:timeout> 1933 <D:locktoken><D:href>$lock[token]</D:href></D:locktoken> 1940 return $activelocks;
1952 if ($status ===
true) {
1958 $this->_http_status = $status;
1961 header(
"HTTP/1.1 $status");
1962 header(
"X-WebDAV-Status: $status",
true);
2011 switch (strtolower($this->_prop_encoding)) {
2018 return utf8_encode(
$text);
2050 .
' DAV Server.' . str_replace(
"\n",
";",
$message)
http_OPTIONS()
GET implementation.
http_PROPPATCH()
PROPPATCH method handler.
_get_ranges(&$options)
parse HTTP Range: header
if(isset($_REQUEST['delete'])) $list
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.
$stream
PHP stream implementation.
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(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
catch(Exception $e) $message
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.
if(!isset($_REQUEST['ReturnTo'])) if(!isset($_REQUEST['AuthId'])) $options
writelog($message)
Writes a message to the logfile.,.