From: Thomas Müller Date: Mon, 7 Oct 2013 15:47:54 +0000 (+0200) Subject: Merge branch 'master' into fixing-4546-master X-Git-Tag: v6.0.0alpha2~11^2~2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5e397d89c4152aee48692b1d302f1f11b16838b3;p=nextcloud-server.git Merge branch 'master' into fixing-4546-master Conflicts: lib/private/connector/sabre/directory.php --- 5e397d89c4152aee48692b1d302f1f11b16838b3 diff --cc lib/private/cache/file.php index 00000000000,2ab914d17b8..b0738d2a92b mode 000000,100644..100644 --- a/lib/private/cache/file.php +++ b/lib/private/cache/file.php @@@ -1,0 -1,118 +1,136 @@@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + namespace OC\Cache; + + class File { + protected $storage; + protected function getStorage() { + if (isset($this->storage)) { + return $this->storage; + } + if(\OC_User::isLoggedIn()) { + \OC\Files\Filesystem::initMountPoints(\OC_User::getUser()); + $subdir = 'cache'; + $view = new \OC\Files\View('/' . \OC_User::getUser()); + if(!$view->file_exists($subdir)) { + $view->mkdir($subdir); + } + $this->storage = new \OC\Files\View('/' . \OC_User::getUser().'/'.$subdir); + return $this->storage; + }else{ + \OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR); + return false; + } + } + + public function get($key) { + $result = null; + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + if ($this->hasKey($key)) { + $storage = $this->getStorage(); + $result = $storage->file_get_contents($key); + } + \OC_FileProxy::$enabled = $proxyStatus; + return $result; + } + ++ /** ++ * Returns the size of the stored/cached data ++ * ++ * @param $key ++ * @return int ++ */ ++ public function size($key) { ++ $result = 0; ++ $proxyStatus = \OC_FileProxy::$enabled; ++ \OC_FileProxy::$enabled = false; ++ if ($this->hasKey($key)) { ++ $storage = $this->getStorage(); ++ $result = $storage->filesize($key); ++ } ++ \OC_FileProxy::$enabled = $proxyStatus; ++ return $result; ++ } ++ + public function set($key, $value, $ttl=0) { + $storage = $this->getStorage(); + $result = false; + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; + if ($storage and $storage->file_put_contents($key, $value)) { + if ($ttl === 0) { + $ttl = 86400; // 60*60*24 + } + $result = $storage->touch($key, time() + $ttl); + } + \OC_FileProxy::$enabled = $proxyStatus; + return $result; + } + + public function hasKey($key) { + $storage = $this->getStorage(); + if ($storage && $storage->is_file($key)) { + $mtime = $storage->filemtime($key); + if ($mtime < time()) { + $storage->unlink($key); + return false; + } + return true; + } + return false; + } + + public function remove($key) { + $storage = $this->getStorage(); + if(!$storage) { + return false; + } + return $storage->unlink($key); + } + + public function clear($prefix='') { + $storage = $this->getStorage(); + if($storage and $storage->is_dir('/')) { + $dh=$storage->opendir('/'); + if(is_resource($dh)) { + while (($file = readdir($dh)) !== false) { + if($file!='.' and $file!='..' and ($prefix==='' || strpos($file, $prefix) === 0)) { + $storage->unlink('/'.$file); + } + } + } + } + return true; + } + + public function gc() { + $storage = $this->getStorage(); + if($storage and $storage->is_dir('/')) { + $now = time(); + $dh=$storage->opendir('/'); + if(!is_resource($dh)) { + return null; + } + while (($file = readdir($dh)) !== false) { + if($file!='.' and $file!='..') { + $mtime = $storage->filemtime('/'.$file); + if ($mtime < $now) { + $storage->unlink('/'.$file); + } + } + } + } + } + + public static function loginListener() { + $c = new self(); + $c->gc(); + } + } diff --cc lib/private/connector/sabre/directory.php index 00000000000,af0dfd70f08..531b0a807bf mode 000000,100644..100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@@ -1,0 -1,218 +1,218 @@@ + . + * + */ + + 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(); + } + + $path = $this->path . '/' . $name; + $node = new OC_Connector_Sabre_File($path); + return $node->put($data); - + } + + /** + * 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 + * @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; + } ++ + } diff --cc lib/private/filechunking.php index 00000000000,313a6ee87d2..0dfce696cda mode 000000,100644..100644 --- a/lib/private/filechunking.php +++ b/lib/private/filechunking.php @@@ -1,0 -1,149 +1,170 @@@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + + class OC_FileChunking { + protected $info; + protected $cache; + + static public function decodeName($name) { + preg_match('/(?P.*)-chunking-(?P\d+)-(?P\d+)-(?P\d+)/', $name, $matches); + return $matches; + } + + public function __construct($info) { + $this->info = $info; + } + + public function getPrefix() { + $name = $this->info['name']; + $transferid = $this->info['transferid']; + + return $name.'-chunking-'.$transferid.'-'; + } + + protected function getCache() { + if (!isset($this->cache)) { + $this->cache = new \OC\Cache\File(); + } + return $this->cache; + } + ++ /** ++ * Stores the given $data under the given $key - the number of stored bytes is returned ++ * ++ * @param $index ++ * @param $data ++ * @return int ++ */ + public function store($index, $data) { + $cache = $this->getCache(); + $name = $this->getPrefix().$index; + $cache->set($name, $data); ++ ++ return $cache->size($name); + } + + public function isComplete() { + $prefix = $this->getPrefix(); + $parts = 0; + $cache = $this->getCache(); + for($i=0; $i < $this->info['chunkcount']; $i++) { + if ($cache->hasKey($prefix.$i)) { + $parts ++; + } + } + return $parts == $this->info['chunkcount']; + } + + public function assemble($f) { + $cache = $this->getCache(); + $prefix = $this->getPrefix(); + $count = 0; + for($i=0; $i < $this->info['chunkcount']; $i++) { + $chunk = $cache->get($prefix.$i); - $cache->remove($prefix.$i); + $count += fwrite($f, $chunk); + } ++ ++ $this->cleanup(); + return $count; + } + ++ /** ++ * Removes all chunks which belong to this transmission ++ */ ++ public function cleanup() { ++ $cache = $this->getCache(); ++ $prefix = $this->getPrefix(); ++ for($i=0; $i < $this->info['chunkcount']; $i++) { ++ $cache->remove($prefix.$i); ++ } ++ } ++ + public function signature_split($orgfile, $input) { + $info = unpack('n', fread($input, 2)); + $blocksize = $info[1]; + $this->info['transferid'] = mt_rand(); + $count = 0; + $needed = array(); + $cache = $this->getCache(); + $prefix = $this->getPrefix(); + while (!feof($orgfile)) { + $new_md5 = fread($input, 16); + if (feof($input)) { + break; + } + $data = fread($orgfile, $blocksize); + $org_md5 = md5($data, true); + if ($org_md5 == $new_md5) { + $cache->set($prefix.$count, $data); + } else { + $needed[] = $count; + } + $count++; + } + return array( + 'transferid' => $this->info['transferid'], + 'needed' => $needed, + 'count' => $count, + ); + } + + public function file_assemble($path) { + $absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path)); + $data = ''; + // use file_put_contents as method because that best matches what this function does + if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) + && \OC\Files\Filesystem::isValidPath($path)) { + $path = \OC\Files\Filesystem::getView()->getRelativePath($absolutePath); + $exists = \OC\Files\Filesystem::file_exists($path); + $run = true; + if(!$exists) { + OC_Hook::emit( + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_create, + array( + \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_run => &$run + ) + ); + } + OC_Hook::emit( + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_write, + array( + \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_run => &$run + ) + ); + if(!$run) { + return false; + } + $target = \OC\Files\Filesystem::fopen($path, 'w'); + if($target) { + $count = $this->assemble($target); + fclose($target); + if(!$exists) { + OC_Hook::emit( + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_post_create, + array( \OC\Files\Filesystem::signal_param_path => $path) + ); + } + OC_Hook::emit( + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_post_write, + array( \OC\Files\Filesystem::signal_param_path => $path) + ); + OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); + return $count > 0; + }else{ + return false; + } + } + } + }