--- /dev/null
- if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
- $info = OC_FileChunking::decodeName($name);
- if (empty($info)) {
- throw new Sabre_DAV_Exception_NotImplemented();
- }
- $chunk_handler = new OC_FileChunking($info);
- $chunk_handler->store($info['index'], $data);
- if ($chunk_handler->isComplete()) {
- $newPath = $this->path . '/' . $info['name'];
- $chunk_handler->file_assemble($newPath);
- return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
- }
- } else {
- $newPath = $this->path . '/' . $name;
-
- // mark file as partial while uploading (ignored by the scanner)
- $partpath = $newPath . '.part';
-
- \OC\Files\Filesystem::file_put_contents($partpath, $data);
-
- // rename to correct path
- $renameOkay = \OC\Files\Filesystem::rename($partpath, $newPath);
- $fileExists = \OC\Files\Filesystem::file_exists($newPath);
- if ($renameOkay === false || $fileExists === false) {
- \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
- \OC\Files\Filesystem::unlink($partpath);
- throw new Sabre_DAV_Exception();
- }
-
- // allow sync clients to send the mtime along in a header
- $mtime = OC_Request::hasModificationTime();
- if ($mtime !== false) {
- if(\OC\Files\Filesystem::touch($newPath, $mtime)) {
- header('X-OC-MTime: accepted');
- }
- }
-
- return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
- }
+ <?php
+
+ /**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+ /**
+ * Creates a new file in the directory
+ *
+ * Data will either be supplied as a stream resource, or in certain cases
+ * as a string. Keep in mind that you may have to support either.
+ *
+ * After succesful creation of the file, you may choose to return the ETag
+ * of the new file here.
+ *
+ * The returned ETag must be surrounded by double-quotes (The quotes should
+ * be part of the actual string).
+ *
+ * If you cannot accurately determine the ETag, you should not return it.
+ * If you don't store the file exactly as-is (you're transforming it
+ * somehow) you should also not return an ETag.
+ *
+ * This means that if a subsequent GET to this new file does not exactly
+ * return the same contents of what was submitted here, you are strongly
+ * recommended to omit the ETag.
+ *
+ * @param string $name Name of the file
+ * @param resource|string $data Initial payload
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return null|string
+ */
+ public function createFile($name, $data = null) {
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
- return null;
++ $path = $this->path . '/' . $name;
++ $node = new OC_Connector_Sabre_File($path);
++ return $node->put($data);
+
- * @return void
+ }
+
+ /**
+ * Creates a new subdirectory
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ if (!\OC\Files\Filesystem::isCreatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ $newPath = $this->path . '/' . $name;
+ if(!\OC\Files\Filesystem::mkdir($newPath)) {
+ throw new Sabre_DAV_Exception_Forbidden('Could not create directory '.$newPath);
+ }
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * @param string $name
+ * @throws Sabre_DAV_Exception_FileNotFound
+ * @return Sabre_DAV_INode
+ */
+ public function getChild($name, $info = null) {
+
+ $path = $this->path . '/' . $name;
+ if (is_null($info)) {
+ $info = \OC\Files\Filesystem::getFileInfo($path);
+ }
+
+ if (!$info) {
+ throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located');
+ }
+
+ if ($info['mimetype'] == 'httpd/unix-directory') {
+ $node = new OC_Connector_Sabre_Directory($path);
+ } else {
+ $node = new OC_Connector_Sabre_File($path);
+ }
+
+ $node->setFileinfoCache($info);
+ return $node;
+ }
+
+ /**
+ * Returns an array with all the child nodes
+ *
+ * @return Sabre_DAV_INode[]
+ */
+ public function getChildren() {
+
+ $folder_content = \OC\Files\Filesystem::getDirectoryContent($this->path);
+ $paths = array();
+ foreach($folder_content as $info) {
+ $paths[] = $this->path.'/'.$info['name'];
+ $properties[$this->path.'/'.$info['name']][self::GETETAG_PROPERTYNAME] = '"' . $info['etag'] . '"';
+ }
+ if(count($paths)>0) {
+ //
+ // the number of arguments within IN conditions are limited in most databases
+ // we chunk $paths into arrays of 200 items each to meet this criteria
+ //
+ $chunks = array_chunk($paths, 200, false);
+ foreach ($chunks as $pack) {
+ $placeholders = join(',', array_fill(0, count($pack), '?'));
+ $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*properties`'
+ .' WHERE `userid` = ?' . ' AND `propertypath` IN ('.$placeholders.')' );
+ array_unshift($pack, OC_User::getUser()); // prepend userid
+ $result = $query->execute( $pack );
+ while($row = $result->fetchRow()) {
+ $propertypath = $row['propertypath'];
+ $propertyname = $row['propertyname'];
+ $propertyvalue = $row['propertyvalue'];
+ if($propertyname !== self::GETETAG_PROPERTYNAME) {
+ $properties[$propertypath][$propertyname] = $propertyvalue;
+ }
+ }
+ }
+ }
+
+ $nodes = array();
+ foreach($folder_content as $info) {
+ $node = $this->getChild($info['name'], $info);
+ $node->setPropertyCache($properties[$this->path.'/'.$info['name']]);
+ $nodes[] = $node;
+ }
+ return $nodes;
+ }
+
+ /**
+ * Checks if a child exists.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function childExists($name) {
+
+ $path = $this->path . '/' . $name;
+ return \OC\Files\Filesystem::file_exists($path);
+
+ }
+
+ /**
+ * Deletes all files in this directory, and then itself
+ *
+ * @return void
+ * @throws Sabre_DAV_Exception_Forbidden
+ */
+ public function delete() {
+
+ if (!\OC\Files\Filesystem::isDeletable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ if ($this->path != "/Shared") {
+ \OC\Files\Filesystem::rmdir($this->path);
+ }
+
+ }
+
+ /**
+ * Returns available diskspace information
+ *
+ * @return array
+ */
+ public function getQuotaInfo() {
+ $storageInfo = OC_Helper::getStorageInfo($this->path);
+ return array(
+ $storageInfo['used'],
+ $storageInfo['free']
+ );
+
+ }
+
+ /**
+ * Returns a list of properties for this nodes.;
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded as xmlnamespace#tagName, for example:
+ * http://www.example.org/namespace#author
+ * If the array is empty, all properties should be returned
+ *
+ * @param array $properties
- $props[self::GETETAG_PROPERTYNAME]
- = OC_Connector_Sabre_Node::getETagPropertyForPath($this->path);
++ * @return array
+ */
+ public function getProperties($properties) {
+ $props = parent::getProperties($properties);
+ if (in_array(self::GETETAG_PROPERTYNAME, $properties) && !isset($props[self::GETETAG_PROPERTYNAME])) {
++ $props[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path);
+ }
+ return $props;
+ }
+ }
--- /dev/null
- * After a succesful put operation, you may choose to return an ETag. The
+ <?php
+
+ /**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_DAV_IFile {
+
+ /**
+ * Updates the data
+ *
+ * The data argument is a readable stream resource.
+ *
-
- if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
++ * After a successful put operation, you may choose to return an ETag. The
+ * etag must always be surrounded by double-quotes. These quotes must
+ * appear in the actual string you're returning.
+ *
+ * Clients may use the ETag from a PUT request to later on make sure that
+ * when they update the file, the contents haven't changed in the mean
+ * time.
+ *
+ * If you don't plan to store the file byte-by-byte, and you return a
+ * different object on a subsequent GET you are strongly recommended to not
+ * return an ETag, and just return null.
+ *
+ * @param resource $data
+ * @throws Sabre_DAV_Exception_Forbidden
+ * @return string|null
+ */
+ public function put($data) {
-
++ $fs = $this->getFS();
++ if ($fs->file_exists($this->path) &&
++ !$fs->isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ // throw an exception if encryption was disabled but the files are still encrypted
+ if (\OC_Util::encryptedFiles()) {
+ throw new \Sabre_DAV_Exception_ServiceUnavailable();
+ }
- \OC\Files\Filesystem::file_put_contents($partpath, $data);
-
- //detect aborted upload
- if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
- if (isset($_SERVER['CONTENT_LENGTH'])) {
- $expected = $_SERVER['CONTENT_LENGTH'];
- $actual = \OC\Files\Filesystem::filesize($partpath);
- if ($actual != $expected) {
- \OC\Files\Filesystem::unlink($partpath);
- throw new Sabre_DAV_Exception_BadRequest(
- 'expected filesize ' . $expected . ' got ' . $actual);
- }
++
++ // chunked handling
++ if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
++ list(, $name) = \Sabre_DAV_URLUtil::splitPath($this->path);
++
++ $info = OC_FileChunking::decodeName($name);
++ if (empty($info)) {
++ throw new Sabre_DAV_Exception_NotImplemented();
++ }
++ $chunk_handler = new OC_FileChunking($info);
++ $chunk_handler->store($info['index'], $data);
++ if ($chunk_handler->isComplete()) {
++ $newPath = $this->path . '/' . $info['name'];
++ $chunk_handler->file_assemble($newPath);
++ return $this->getETagPropertyForPath($newPath);
++ }
++
++ return null;
++ }
++
+ // mark file as partial while uploading (ignored by the scanner)
+ $partpath = $this->path . '.part';
+
- $renameOkay = \OC\Files\Filesystem::rename($partpath, $this->path);
- $fileExists = \OC\Files\Filesystem::file_exists($this->path);
++ try {
++ $putOkay = $fs->file_put_contents($partpath, $data);
++ if ($putOkay === false) {
++ \OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR);
++ $fs->unlink($partpath);
++ // because we have no clue about the cause we can only throw back a 500/Internal Server Error
++ throw new Sabre_DAV_Exception();
+ }
++ } catch (\OCP\Files\NotPermittedException $e) {
++ throw new Sabre_DAV_Exception_Forbidden();
+ }
+
+ // rename to correct path
- \OC\Files\Filesystem::unlink($partpath);
++ $renameOkay = $fs->rename($partpath, $this->path);
++ $fileExists = $fs->file_exists($this->path);
+ if ($renameOkay === false || $fileExists === false) {
+ \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR);
-
- //allow sync clients to send the mtime along in a header
++ $fs->unlink($partpath);
+ throw new Sabre_DAV_Exception();
+ }
+
- if (\OC\Files\Filesystem::touch($this->path, $mtime)) {
++ // allow sync clients to send the mtime along in a header
+ $mtime = OC_Request::hasModificationTime();
+ if ($mtime !== false) {
- return OC_Connector_Sabre_Node::getETagPropertyForPath($this->path);
++ if($fs->touch($this->path, $mtime)) {
+ header('X-OC-MTime: accepted');
+ }
+ }
+
- //throw execption if encryption is disabled but files are still encrypted
++ return $this->getETagPropertyForPath($this->path);
+ }
+
+ /**
+ * Returns the data
+ *
+ * @return string
+ */
+ public function get() {
+
- * arbritrary string, but MUST be surrounded by double-quotes.
++ //throw exception if encryption is disabled but files are still encrypted
+ if (\OC_Util::encryptedFiles()) {
+ throw new \Sabre_DAV_Exception_ServiceUnavailable();
+ } else {
+ return \OC\Files\Filesystem::fopen($this->path, 'rb');
+ }
+
+ }
+
+ /**
+ * Delete the current file
+ *
+ * @return void
+ * @throws Sabre_DAV_Exception_Forbidden
+ */
+ public function delete() {
+
+ if (!\OC\Files\Filesystem::isDeletable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+ \OC\Files\Filesystem::unlink($this->path);
+
+ }
+
+ /**
+ * Returns the size of the node, in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+ $this->getFileinfoCache();
+ if ($this->fileinfo_cache['size'] > -1) {
+ return $this->fileinfo_cache['size'];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the ETag for a file
+ *
+ * An ETag is a unique identifier representing the current version of the
+ * file. If the file changes, the ETag MUST change. The ETag is an
++ * arbitrary string, but MUST be surrounded by double-quotes.
+ *
+ * Return null if the ETag can not effectively be determined
+ *
+ * @return mixed
+ */
+ public function getETag() {
+ $properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME));
+ if (isset($properties[self::GETETAG_PROPERTYNAME])) {
+ return $properties[self::GETETAG_PROPERTYNAME];
+ }
+ return null;
+ }
+
+ /**
+ * Returns the mime-type for a file
+ *
+ * If null is returned, we'll assume application/octet-stream
+ *
+ * @return mixed
+ */
+ public function getContentType() {
+ if (isset($this->fileinfo_cache['mimetype'])) {
+ return $this->fileinfo_cache['mimetype'];
+ }
+
+ return \OC\Files\Filesystem::getMimeType($this->path);
+
+ }
+ }
--- /dev/null
- static public function getETagPropertyForPath($path) {
- $data = \OC\Files\Filesystem::getFileInfo($path);
+ <?php
+
+ /**
+ * ownCloud
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties {
+ const GETETAG_PROPERTYNAME = '{DAV:}getetag';
+ const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
+
+ /**
+ * Allow configuring the method used to generate Etags
+ *
+ * @var array(class_name, function_name)
+ */
+ public static $ETagFunction = null;
+
++ /**
++ * is kept public to allow overwrite for unit testing
++ *
++ * @var \OC\Files\View
++ */
++ public $fileView;
++
+ /**
+ * The path to the current node
+ *
+ * @var string
+ */
+ protected $path;
+ /**
+ * node fileinfo cache
+ * @var array
+ */
+ protected $fileinfo_cache;
+ /**
+ * node properties cache
+ * @var array
+ */
+ protected $property_cache = null;
+
+ /**
+ * @brief Sets up the node, expects a full path name
+ * @param string $path
+ * @return void
+ */
+ public function __construct($path) {
+ $this->path = $path;
+ }
+
+
+
+ /**
+ * @brief Returns the name of the node
+ * @return string
+ */
+ public function getName() {
+
+ list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path);
+ return $name;
+
+ }
+
+ /**
+ * @brief Renames the node
+ * @param string $name The new name
+ * @return void
+ */
+ public function setName($name) {
+
+ // rename is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+
+ $newPath = $parentPath . '/' . $newName;
+ $oldPath = $this->path;
+
+ \OC\Files\Filesystem::rename($this->path, $newPath);
+
+ $this->path = $newPath;
+
+ $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?'
+ .' WHERE `userid` = ? AND `propertypath` = ?' );
+ $query->execute( array( $newPath, OC_User::getUser(), $oldPath ));
+
+ }
+
+ public function setFileinfoCache($fileinfo_cache)
+ {
+ $this->fileinfo_cache = $fileinfo_cache;
+ }
+
+ /**
+ * @brief Ensure that the fileinfo cache is filled
+ * @note Uses OC_FileCache or a direct stat
+ */
+ protected function getFileinfoCache() {
+ if (!isset($this->fileinfo_cache)) {
+ if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) {
+ } else {
+ $fileinfo_cache = \OC\Files\Filesystem::stat($this->path);
+ }
+
+ $this->fileinfo_cache = $fileinfo_cache;
+ }
+ }
+
+ public function setPropertyCache($property_cache)
+ {
+ $this->property_cache = $property_cache;
+ }
+
+ /**
+ * @brief Returns the last modification time, as a unix timestamp
+ * @return int
+ */
+ public function getLastModified() {
+ $this->getFileinfoCache();
+ return $this->fileinfo_cache['mtime'];
+
+ }
+
+ /**
+ * sets the last modification time of the file (mtime) to the value given
+ * in the second parameter or to now if the second param is empty.
+ * Even if the modification time is set to a custom value the access time is set to now.
+ */
+ public function touch($mtime) {
+
+ // touch is only allowed if the update privilege is granted
+ if (!\OC\Files\Filesystem::isUpdatable($this->path)) {
+ throw new \Sabre_DAV_Exception_Forbidden();
+ }
+
+ \OC\Files\Filesystem::touch($this->path, $mtime);
+ }
+
+ /**
+ * @brief Updates properties on this node,
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateProperties($properties) {
+ $existing = $this->getProperties(array());
+ foreach($properties as $propertyName => $propertyValue) {
+ // If it was null, we need to delete the property
+ if (is_null($propertyValue)) {
+ if(array_key_exists( $propertyName, $existing )) {
+ $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`'
+ .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
+ $query->execute( array( OC_User::getUser(), $this->path, $propertyName ));
+ }
+ }
+ else {
+ if( strcmp( $propertyName, self::GETETAG_PROPERTYNAME) === 0 ) {
+ \OC\Files\Filesystem::putFileInfo($this->path, array('etag'=> $propertyValue));
+ } elseif( strcmp( $propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0 ) {
+ $this->touch($propertyValue);
+ } else {
+ if(!array_key_exists( $propertyName, $existing )) {
+ $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties`'
+ .' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' );
+ $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
+ } else {
+ $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?'
+ .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' );
+ $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
+ }
+ }
+ }
+
+ }
+ $this->setPropertyCache(null);
+ return true;
+ }
+
+ /**
+ * @brief Returns a list of properties for this nodes.;
+ * @param array $properties
+ * @return array
+ * @note The properties list is a list of propertynames the client
+ * requested, encoded as xmlnamespace#tagName, for example:
+ * http://www.example.org/namespace#author If the array is empty, all
+ * properties should be returned
+ */
+ public function getProperties($properties) {
+ if (is_null($this->property_cache)) {
+ $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
+ $result = OC_DB::executeAudited( $sql, array( OC_User::getUser(), $this->path ) );
+
+ $this->property_cache = array();
+ while( $row = $result->fetchRow()) {
+ $this->property_cache[$row['propertyname']] = $row['propertyvalue'];
+ }
+
+ // Don't call the static getETagPropertyForPath, its result is not cached
+ $this->getFileinfoCache();
+ if ($this->fileinfo_cache['etag']) {
+ $this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"';
+ } else {
+ $this->property_cache[self::GETETAG_PROPERTYNAME] = null;
+ }
+ }
+
+ // if the array was empty, we need to return everything
+ if(count($properties) == 0) {
+ return $this->property_cache;
+ }
+
+ $props = array();
+ foreach($properties as $property) {
+ if (isset($this->property_cache[$property])) $props[$property] = $this->property_cache[$property];
+ }
+ return $props;
+ }
+
+ /**
+ * Returns the ETag surrounded by double-quotes for this path.
+ * @param string $path Path of the file
+ * @return string|null Returns null if the ETag can not effectively be determined
+ */
++ protected function getETagPropertyForPath($path) {
++ $data = $this->getFS()->getFileInfo($path);
+ if (isset($data['etag'])) {
+ return '"'.$data['etag'].'"';
+ }
+ return null;
+ }
+
++ protected function getFS() {
++ if (is_null($this->fileView)) {
++ $this->fileView = \OC\Files\Filesystem::getView();
++ }
++ return $this->fileView;
++ }
+ }