📄 server.php
字号:
case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16 // single byte range requests are supported // the header format is also specified in RFC 2616 14.16 // TODO we have to ensure that implementations support this or send 501 instead if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $val, $matches)) { $this->http_status("400 bad request"); echo "The service does only support single byte ranges"; return; } $range = array("start"=>$matches[1], "end"=>$matches[2]); if (is_numeric($matches[3])) { $range["total_length"] = $matches[3]; } $option["ranges"][] = $range; // TODO make sure the implementation supports partial PUT // this has to be done in advance to avoid data being overwritten // on implementations that do not support this ... break; case 'HTTP_CONTENT_MD5': // RFC 2616 14.15 // TODO: maybe we can just pretend here? $this->http_status("501 not implemented"); echo "The service does not support content MD5 checksum verification"; return; default: // any other unknown Content-* headers $this->http_status("501 not implemented"); echo "The service does not support '$key'"; return; } } $options["stream"] = fopen("php://input", "r"); $stat = $this->PUT($options); if ($stat === false) { $stat = "403 Forbidden"; } else if (is_resource($stat) && get_resource_type($stat) == "stream") { $stream = $stat; $stat = $options["new"] ? "201 Created" : "204 No Content"; if (!empty($options["ranges"])) { // TODO multipart support is missing (see also above) if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) { $length = $range[0]["end"]-$range[0]["start"]+1; if (!fwrite($stream, fread($options["stream"], $length))) { $stat = "403 Forbidden"; } } else { $stat = "403 Forbidden"; } } else { while (!feof($options["stream"])) { if (false === fwrite($stream, fread($options["stream"], 4096))) { $stat = "403 Forbidden"; break; } } } fclose($stream); } $this->http_status($stat); } else { $this->http_status("423 Locked"); } } // }}} // {{{ http_DELETE() /** * DELETE method handler * * @param void * @return void */ function http_DELETE() { // check RFC 2518 Section 9.2, last paragraph if (isset($this->_SERVER["HTTP_DEPTH"])) { if ($this->_SERVER["HTTP_DEPTH"] != "infinity") { $this->http_status("400 Bad Request"); return; } } // check lock status if ($this->_check_lock_status($this->path)) { // ok, proceed $options = Array(); $options["path"] = $this->path; $stat = $this->DELETE($options); $this->http_status($stat); } else { // sorry, its locked $this->http_status("423 Locked"); } } // }}} // {{{ http_COPY() /** * COPY method handler * * @param void * @return void */ function http_COPY() { // no need to check source lock status here // destination lock status is always checked by the helper method $this->_copymove("copy"); } // }}} // {{{ http_MOVE() /** * MOVE method handler * * @param void * @return void */ function http_MOVE() { if ($this->_check_lock_status($this->path)) { // destination lock status is always checked by the helper method $this->_copymove("move"); } else { $this->http_status("423 Locked"); } } // }}} // {{{ http_LOCK() /** * LOCK method handler * * @param void * @return void */ function http_LOCK() { $options = Array(); $options["path"] = $this->path; if (isset($this->_SERVER['HTTP_DEPTH'])) { $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; } else { $options["depth"] = "infinity"; } if (isset($this->_SERVER["HTTP_TIMEOUT"])) { $options["timeout"] = explode(",", $this->_SERVER["HTTP_TIMEOUT"]); } if (empty($this->_SERVER['CONTENT_LENGTH']) && !empty($this->_SERVER['HTTP_IF'])) { // check if locking is possible if (!$this->_check_lock_status($this->path)) { $this->http_status("423 Locked"); return; } // refresh lock $options["locktoken"] = substr($this->_SERVER['HTTP_IF'], 2, -2); $options["update"] = $options["locktoken"]; // setting defaults for required fields, LOCK() SHOULD overwrite these $options['owner'] = "unknown"; $options['scope'] = "exclusive"; $options['type'] = "write"; $stat = $this->LOCK($options); } else { // extract lock request information from request XML payload $lockinfo = new _parse_lockinfo("php://input"); if (!$lockinfo->success) { $this->http_status("400 bad request"); } // check if locking is possible if (!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) { $this->http_status("423 Locked"); return; } // new lock $options["scope"] = $lockinfo->lockscope; $options["type"] = $lockinfo->locktype; $options["owner"] = $lockinfo->owner; $options["locktoken"] = $this->_new_locktoken(); $stat = $this->LOCK($options); } if (is_bool($stat)) { $http_stat = $stat ? "200 OK" : "423 Locked"; } else { $http_stat = $stat; } $this->http_status($http_stat); if ($http_stat{0} == 2) { // 2xx states are ok if ($options["timeout"]) { // if multiple timeout values were given we take the first only if (is_array($options["timeout"])) { reset($options["timeout"]); $options["timeout"] = current($options["timeout"]); } // if the timeout is numeric only we need to reformat it if (is_numeric($options["timeout"])) { // more than a million is considered an absolute timestamp // less is more likely a relative value if ($options["timeout"]>1000000) { $timeout = "Second-".($options['timeout']-time()); } else { $timeout = "Second-$options[timeout]"; } } else { // non-numeric values are passed on verbatim, // no error checking is performed here in this case // TODO: send "Infinite" on invalid timeout strings? $timeout = $options["timeout"]; } } else { $timeout = "Infinite"; } header('Content-Type: text/xml; charset="utf-8"'); header("Lock-Token: <$options[locktoken]>"); echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; echo "<D:prop xmlns:D=\"DAV:\">\n"; echo " <D:lockdiscovery>\n"; echo " <D:activelock>\n"; echo " <D:lockscope><D:$options[scope]/></D:lockscope>\n"; echo " <D:locktype><D:$options[type]/></D:locktype>\n"; echo " <D:depth>$options[depth]</D:depth>\n"; echo " <D:owner>$options[owner]</D:owner>\n"; echo " <D:timeout>$timeout</D:timeout>\n"; echo " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n"; echo " </D:activelock>\n"; echo " </D:lockdiscovery>\n"; echo "</D:prop>\n\n"; } } // }}} // {{{ http_UNLOCK() /** * UNLOCK method handler * * @param void * @return void */ function http_UNLOCK() { $options = Array(); $options["path"] = $this->path; if (isset($this->_SERVER['HTTP_DEPTH'])) { $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; } else { $options["depth"] = "infinity"; } // strip surrounding <> $options["token"] = substr(trim($this->_SERVER["HTTP_LOCK_TOKEN"]), 1, -1); // call user method $stat = $this->UNLOCK($options); $this->http_status($stat); } // }}} // }}} // {{{ _copymove() function _copymove($what) { $options = Array(); $options["path"] = $this->path; if (isset($this->_SERVER["HTTP_DEPTH"])) { $options["depth"] = $this->_SERVER["HTTP_DEPTH"]; } else { $options["depth"] = "infinity"; } $http_header_host = preg_replace("/:80$/", "", $this->_SERVER["HTTP_HOST"]); $url = parse_url($this->_SERVER["HTTP_DESTINATION"]); $path = urldecode($url["path"]); if (isset($url["host"])) { // TODO check url scheme, too $http_host = $url["host"]; if (isset($url["port"]) && $url["port"] != 80) $http_host.= ":".$url["port"]; } else { // only path given, set host to self $http_host == $http_header_host; } if ($http_host == $http_header_host && !strncmp($this->_SERVER["SCRIPT_NAME"], $path, strlen($this->_SERVER["SCRIPT_NAME"]))) { $options["dest"] = substr($path, strlen($this->_SERVER["SCRIPT_NAME"])); if (!$this->_check_lock_status($options["dest"])) { $this->http_status("423 Locked"); return; } } else { $options["dest_url"] = $this->_SERVER["HTTP_DESTINATION"]; } // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3 if (isset($this->_SERVER["HTTP_OVERWRITE"])) { $options["overwrite"] = $this->_SERVER["HTTP_OVERWRITE"] == "T"; } else { $options["overwrite"] = true; } $stat = $this->$what($options); $this->http_status($stat); } // }}} // {{{ _allow() /** * check for implemented HTTP methods * * @param void * @return array something */ function _allow() { // OPTIONS is always there $allow = array("OPTIONS" =>"OPTIONS"); // all other METHODS need both a http_method() wrapper // and a method() implementation // the base class supplies wrappers only foreach (get_class_methods($this) as $method) { if (!strncmp("http_", $method, 5)) { $method = strtoupper(substr($method, 5)); if (method_exists($this, $method)) { $allow[$method] = $method; } } } // we can emulate a missing HEAD implemetation using GET if (isset($allow["GET"])) $allow["HEAD"] = "HEAD"; // no LOCK without checklok() if (!method_exists($this, "checklock")) { unset($allow["LOCK"]); unset($allow["UNLOCK"]); } return $allow; } // }}} /** * helper for property element creation * * @param string XML namespace (optional) * @param string property name * @param string property value * @return array property array */ function mkprop()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -