From 038211793958c0c90eac63339f2d1d60b5f2af0a Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 26 Nov 2013 18:11:52 +0100 Subject: [PATCH 1/2] Fix update detection for Dropbox (WIP) Now using Dropbox hash as etag. The current solution is clumsy and unefficient. --- apps/files_external/lib/dropbox.php | 36 +++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index f7d8d98cf033..2320697dcd17 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -63,17 +63,18 @@ private function deleteMetaData($path) { * @brief Returns the path's metadata * @param $path path for which to return the metadata * @param $list if true, also return the directory's contents + * @param $hash previously known hash, used to detect changes * @return directory contents if $list is true, file metadata if $list is * false, null if the file doesn't exist or "false" if the operation failed */ - private function getMetaData($path, $list = false) { + private function getMetaData($path, $list = false, $hash = null) { $path = $this->root.$path; - if ( ! $list && isset($this->metaData[$path])) { + if ( ! $list && ! $hash && isset($this->metaData[$path])) { return $this->metaData[$path]; } else { if ($list) { try { - $response = $this->dropbox->getMetaData($path); + $response = $this->dropbox->getMetaData($path, 'true', $hash); } catch (\Exception $exception) { \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; @@ -96,7 +97,7 @@ private function getMetaData($path, $list = false) { return $contents; } else { try { - $response = $this->dropbox->getMetaData($path, 'false'); + $response = $this->dropbox->getMetaData($path, 'false', $hash); if (!isset($response['is_deleted']) || !$response['is_deleted']) { $this->metaData[$path] = $response; return $response; @@ -308,4 +309,31 @@ public function touch($path, $mtime = null) { return true; } + public function hasUpdated($path, $time) { + // Dropbox doesn't have directory mtime, so need to use the Dropbox hash + // we stored as etag + $newData = $this->getMetaData($path, false); + // FIXME: might break if someone replaced a dir with a file with + // the same name before we got the update + if ($newData['is_dir']) { + $data = $this->getCache()->get($path); + return $newData['hash'] !== $data['etag']; + } + // use mtime for files + return parent::hasUpdated($path, $time); + } + + public function getETag($path) { + // FIXME: ouch, we need to get the directory contents to + // get the hash value, maybe there is a more efficient way to do this ? + $metaData = $this->getMetaData($path, true); + // then retrieve the cached entry + $metaData = $this->getMetaData($path); + if ($metaData && isset($metaData['is_dir']) && $metaData['is_dir'] && isset($metaData['hash'])) { + // for directories + return $metaData['hash']; + } + // files need to use the regular etag algo (no hash) + return parent::getEtag($path); + } } From 11e15cfcafe82d16b3818344cbcf33f86c1f63e0 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 27 Nov 2013 11:19:02 +0100 Subject: [PATCH 2/2] Now trying to check the hash, but Dropbox doesn't seem to let us... --- apps/files_external/lib/dropbox.php | 43 +++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 2320697dcd17..ef4a7ba607f3 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -65,7 +65,8 @@ private function deleteMetaData($path) { * @param $list if true, also return the directory's contents * @param $hash previously known hash, used to detect changes * @return directory contents if $list is true, file metadata if $list is - * false, null if the file doesn't exist or "false" if the operation failed + * false, null if the file doesn't exist, "false" if the operation failed. + * If a hash is passed, "true" might be returned if the metadata didn't change */ private function getMetaData($path, $list = false, $hash = null) { $path = $this->root.$path; @@ -74,7 +75,14 @@ private function getMetaData($path, $list = false, $hash = null) { } else { if ($list) { try { - $response = $this->dropbox->getMetaData($path, 'true', $hash); + $response = $this->dropbox->getMetaData($path, 'true'); + // FIXME: why does Dropbox rerturn 401 unauthorized when passing a hash !? + //$response = $this->dropbox->getMetaData($path, 'true', $hash); + // FIXME: HACK, checking the hash here + if ($hash && $hash === $response['hash']) { + // no changes + return true; + } } catch (\Exception $exception) { \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; @@ -312,22 +320,39 @@ public function touch($path, $mtime = null) { public function hasUpdated($path, $time) { // Dropbox doesn't have directory mtime, so need to use the Dropbox hash // we stored as etag - $newData = $this->getMetaData($path, false); + $data = $this->getCache()->get($path); // FIXME: might break if someone replaced a dir with a file with // the same name before we got the update - if ($newData['is_dir']) { - $data = $this->getCache()->get($path); - return $newData['hash'] !== $data['etag']; + if ($data['mimetype'] === 'httpd/unix-directory') { + $hash = $data['etag']; + $newData = $this->getMetaData($path, true, $hash); + return $newData !== true; } // use mtime for files return parent::hasUpdated($path, $time); } public function getETag($path) { - // FIXME: ouch, we need to get the directory contents to - // get the hash value, maybe there is a more efficient way to do this ? - $metaData = $this->getMetaData($path, true); // then retrieve the cached entry + $data = $this->getCache()->get($path); + // FIXME: might break if someone replaced a dir with a file with + // the same name before we got the update + if ($data && $data['mimetype'] === 'httpd/unix-directory' && isset($data['etag'])) { + // only check for changes + $metaData = $this->getMetaData($path, true, $data['etag']); + // no changes ? + if ($metaData === true) { + // return old etag + return $data['etag']; + } + } + else { + // FIXME: ouch, we need to get the directory contents to + // get the hash value, maybe there is a more efficient way to do this ? + $metaData = $this->getMetaData($path, true); + } + + // this will get the cached entry for the directory $metaData = $this->getMetaData($path); if ($metaData && isset($metaData['is_dir']) && $metaData['is_dir'] && isset($metaData['hash'])) { // for directories