diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 600eeb1949..ec04e00686 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -775,6 +775,8 @@ class calendar_groupdav extends groupdav_handler if ($schedule_tag_match !== $schedule_tag) { if ($this->debug) error_log(__METHOD__."(,,$user) schedule_tag missmatch: given '$schedule_tag_match' != '$schedule_tag'"); + // honor Prefer: return=representation for 412 too (no need for client to explicitly reload) + $this->check_return_representation($options, $id, $user); return '412 Precondition Failed'; } } @@ -892,6 +894,8 @@ class calendar_groupdav extends groupdav_handler } elseif ($cal_id === 0) // etag failure { + // honor Prefer: return=representation for 412 too (no need for client to explicitly reload) + $this->check_return_representation($options, $id, $user); return '412 Precondition Failed'; } else diff --git a/egw-pear/HTTP/WebDAV/Server.php b/egw-pear/HTTP/WebDAV/Server.php index c59d8ef9e7..d840079d7f 100644 --- a/egw-pear/HTTP/WebDAV/Server.php +++ b/egw-pear/HTTP/WebDAV/Server.php @@ -742,6 +742,13 @@ class HTTP_WebDAV_Server header("DAV: " .join(", ", $dav)); header('Content-Type: text/xml; charset="utf-8"'); + // add Vary and Preference-Applied header for Prefer: return=minimal + if (isset($this->_SERVER['HTTP_PREFER']) && in_array('return=minimal', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER']))) + { + header("Preference-Applied: return=minimal"); + header("Vary: Prefer"); + } + // ... and payload echo "\n"; echo $this->crrnd ? "\n" : "\n"; @@ -777,9 +784,20 @@ class HTTP_WebDAV_Server { $files = new ArrayIterator($files); } + // support for "Prefer: depth-noroot" header on PROPFIND + $skip_root = $this->_SERVER['REQUEST_METHOD'] == 'PROPFIND' && + !isset($initial_ns_hash) && // multistatus_response calls itself, do NOT apply skip in that case + isset($this->_SERVER['HTTP_PREFER']) && in_array('depth-noroot', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER'])); + // now we loop over all returned file entries foreach ($files as $file) { + // skip first element (root), if requested by Prefer: depth-noroot + if ($skip_root) { + $skip_root = false; + continue; + } + // collect namespaces here $ns_hash = $initial_ns_hash; @@ -864,9 +882,9 @@ class HTTP_WebDAV_Server = $this->mkprop("DAV:", "lockdiscovery", $this->lockdiscovery($file['path'])); - // only collect $file['noprops'] if we have NO Brief: t and NO Prefer: return-minimal HTTP Header + // only collect $file['noprops'] if we have NO Brief: t and NO Prefer: return=minimal HTTP Header } elseif ((!isset($this->_SERVER['HTTP_BRIEF']) || $this->_SERVER['HTTP_BRIEF'] != 't') && - (!isset($this->_SERVER['HTTP_PREFER']) || $this->_SERVER['HTTP_PREFER'] != 'return-minimal')) { + (!isset($this->_SERVER['HTTP_PREFER']) || !in_array('return=minimal', preg_split('/, ?/', $this->_SERVER['HTTP_PREFER'])))) { // add empty value for this property $file["noprops"][] = $this->mkprop($reqprop["xmlns"], $reqprop["name"], ""); diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php index 3a893d56a3..d4262c49fe 100644 --- a/phpgwapi/inc/class.groupdav_handler.inc.php +++ b/phpgwapi/inc/class.groupdav_handler.inc.php @@ -292,6 +292,8 @@ abstract class groupdav_handler if ($this->http_if_match !== $etag) { if ($this->debug) error_log(__METHOD__."($method,path=$options[path],$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed".array2string($entry)); + // honor Prefer: return=representation for 412 too (no need for client to explicitly reload) + $this->check_return_representation($options, $id); return '412 Precondition Failed'; } } @@ -310,6 +312,8 @@ abstract class groupdav_handler if ($method == 'PUT' && ($if_none_match == '*' || $if_none_match == $etag)) { if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 412 Precondition failed"); + // honor Prefer: return=representation for 412 too (no need for client to explicitly reload) + $this->check_return_representation($options, $id); return '412 Precondition Failed'; } } @@ -317,6 +321,44 @@ abstract class groupdav_handler return $entry; } + /** + * Return representation, if requested by HTTP Prefer header + * + * @param array $options + * @param int $id + * @param int $user=null account_id + * @return string|boolean http status of get or null if no representation was requested + */ + public function check_return_representation($options, $id, $user=null) + { + //error_log(__METHOD__."(, $id, $user) start ".function_backtrace()); + if (isset($_SERVER['HTTP_PREFER']) && in_array('return=representation', preg_split('/, ?/', $_SERVER['HTTP_PREFER']))) + { + if ($_SERVER['REQUEST_METHOD'] == 'POST') + { + $location = $this->groupdav->base_uri.$options['path']; + if ($location[0] == '/') + { + $location = (@$_SERVER['HTTPS'] === 'on' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].$location; + } + header('Content-Location: '.$location); + } + + // remove If-Match or If-None-Match headers, otherwise HTTP status 412 goes into endless loop! + unset($_SERVER['HTTP_IF_MATCH']); + unset($_SERVER['HTTP_IF_NONE_MATCH']); + + if (($ret = $this->get($options, $id ? $id : $this->new_id, $user)) && !empty($options['data'])) + { + header('Content-Length: '.$this->groupdav->bytes($options['data'])); + header('Content-Type: '.$options['mimetype']); + echo $options['data']; + } + } + //error_log(__METHOD__."(, $id, $user) returning ".array2string($ret)); + return $ret; + } + /** * Get the handler for the given app *