diff options
author | Bart Visscher <bartv@thisnet.nl> | 2012-05-04 17:04:11 +0200 |
---|---|---|
committer | Bart Visscher <bartv@thisnet.nl> | 2012-05-04 17:04:11 +0200 |
commit | 1e471562268ff62c59708b724c22930bc1d01d95 (patch) | |
tree | 5b5125ec41e865fa009d51b4293042f6572067f4 /apps/files_external | |
parent | 71f9b1968e3d4decc4395db2a1555a872cbb2820 (diff) | |
parent | 07ff1e723ae4fa3a0297b168ef2262e01a0a5e50 (diff) | |
download | nextcloud-server-1e471562268ff62c59708b724c22930bc1d01d95.tar.gz nextcloud-server-1e471562268ff62c59708b724c22930bc1d01d95.zip |
Merge branch 'master' into tasks
Diffstat (limited to 'apps/files_external')
-rw-r--r-- | apps/files_external/appinfo/app.php | 1 | ||||
-rw-r--r-- | apps/files_external/appinfo/info.xml | 1 | ||||
-rw-r--r-- | apps/files_external/appinfo/version | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | apps/files_external/lib/ftp.php | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | apps/files_external/lib/google.php | 262 | ||||
-rwxr-xr-x | apps/files_external/lib/swift.php | 531 | ||||
-rwxr-xr-x[-rw-r--r--] | apps/files_external/lib/webdav.php | 2 | ||||
-rw-r--r-- | apps/files_external/tests/config.php | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | apps/files_external/tests/ftp.php | 2 | ||||
-rw-r--r-- | apps/files_external/tests/swift.php | 32 |
10 files changed, 770 insertions, 73 deletions
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 95770b44b75..784ed032d47 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -9,3 +9,4 @@ OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php'; OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php'; OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php'; +OC::$CLASSPATH['OC_Filestorage_SWIFT']='apps/files_external/lib/swift.php'; diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index fb58297ff17..1918925389d 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -3,7 +3,6 @@ <id>files_external</id> <name>External storage support</name> <description>Mount external storage sources</description> - <version>0.1</version> <licence>AGPL</licence> <author>Robin Appelman</author> <require>3</require> diff --git a/apps/files_external/appinfo/version b/apps/files_external/appinfo/version new file mode 100644 index 00000000000..ceab6e11ece --- /dev/null +++ b/apps/files_external/appinfo/version @@ -0,0 +1 @@ +0.1
\ No newline at end of file diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 802446b4fd8..981eeab58bf 100644..100755 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -108,7 +108,7 @@ class OC_FileStorage_FTP extends OC_Filestorage_Common{ }else{ $ext=''; } - $tmpFile=OC_Helper::tmpFile($ext); + $tmpFile=OCP\Files::tmpFile($ext); OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)){ $this->getFile($path,$tmpFile); diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 0d6db1987f8..d2285a6d82c 100644..100755 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -29,6 +29,8 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { private $sig_method; private $entries; + private static $tempFiles = array(); + public function __construct($arguments) { $consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous'; $consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous'; @@ -38,11 +40,11 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $this->entries = array(); } - private function sendRequest($feedUri, $http_method, $isDownload = false, $postData = null) { - $feedUri = trim($feedUri); + private function sendRequest($uri, $httpMethod, $postData = null, $extraHeaders = null, $isDownload = false, $returnHeaders = false, $isContentXML = true) { + $uri = trim($uri); // create an associative array from each key/value url query param pair. $params = array(); - $pieces = explode('?', $feedUri); + $pieces = explode('?', $uri); if (isset($pieces[1])) { $params = explode_assoc('=', '&', $pieces[1]); } @@ -51,16 +53,22 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { foreach ($params as $key => $value) { $tempStr .= '&' . urlencode($key) . '=' . urlencode($value); } - $feedUri = preg_replace('/&/', '?', $tempStr, 1); - $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $http_method, $feedUri, $params); + $uri = preg_replace('/&/', '?', $tempStr, 1); + $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $httpMethod, $uri, $params); $request->sign_request($this->sig_method, $this->consumer, $this->oauth_token); $auth_header = $request->to_header(); - $headers = array($auth_header, 'Content-Type: application/atom+xml', 'GData-Version: 3.0'); - $curl = curl_init($feedUri); + $headers = array($auth_header, 'GData-Version: 3.0'); + if ($isContentXML) { + $headers = array_merge($headers, array('Content-Type: application/atom+xml')); + } + if (is_array($extraHeaders)) { + $headers = array_merge($headers, $extraHeaders); + } + $curl = curl_init($uri); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_FAILONERROR, false); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - switch ($http_method) { + switch ($httpMethod) { case 'GET': curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); break; @@ -72,30 +80,50 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { case 'PUT': $headers[] = 'If-Match: *'; curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpMethod); curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); break; case 'DELETE': $headers[] = 'If-Match: *'; curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpMethod); break; default: curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); } if ($isDownload) { $tmpFile = OC_Helper::tmpFile(); - $fp = fopen($tmpFile, 'w'); - curl_setopt($curl, CURLOPT_FILE, $fp); - curl_exec($curl); - curl_close($curl); - return $tmpFile; + $handle = fopen($tmpFile, 'w'); + curl_setopt($curl, CURLOPT_FILE, $handle); + } + if ($returnHeaders) { + curl_setopt($curl, CURLOPT_HEADER, true); } $result = curl_exec($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); - $dom = new DOMDocument(); - $dom->loadXML($result); - return $dom; + if ($result) { + // TODO https://developers.google.com/google-apps/documents-list/#handling_api_errors + // TODO Log error messages + if ($httpCode <= 308) { + if ($isDownload) { + return $tmpFile; + } else { + return $result; + } + } + } + return false; + } + + private function getFeed($feedUri, $httpMethod, $postData = null) { + $result = $this->sendRequest($feedUri, $httpMethod, $postData); + if ($result) { + $dom = new DOMDocument(); + $dom->loadXML($result); + return $dom; + } + return false; } private function getResource($path) { @@ -106,14 +134,14 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { // Strip the file extension; file could be a native Google Docs resource if ($pos = strpos($file, '.')) { $title = substr($file, 0, $pos); - $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET'); + $dom = $this->getFeed('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$title, 'GET'); // Check if request was successful and entry exists if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) { $this->entries[$file] = $entry; return $entry; } } - $dom = $this->sendRequest('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$file, 'GET'); + $dom = $this->getFeed('https://docs.google.com/feeds/default/private/full?showfolders=true&title='.$file, 'GET'); // Check if request was successful and entry exists if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) { $this->entries[$file] = $entry; @@ -143,15 +171,15 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { public function mkdir($path) { - $dir = dirname($path); + $collection = dirname($path); // Check if path parent is root directory - if ($dir == '/' || $dir == '\.' || $dir == '.') { - $feedUri = 'https://docs.google.com/feeds/default/private/full'; + if ($collection == '/' || $collection == '\.' || $collection == '.') { + $uri = 'https://docs.google.com/feeds/default/private/full'; // Get parent content link } else if ($dom = $this->getResource(basename($dir))) { - $feedUri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src'); + $uri = $dom->getElementsByTagName('content')->item(0)->getAttribute('src'); } - if (isset($feedUri)) { + if (isset($uri)) { $title = basename($path); // Construct post data $postData = '<?xml version="1.0" encoding="UTF-8"?>'; @@ -159,7 +187,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $postData .= '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/docs/2007#folder"/>'; $postData .= '<title>'.$title.'</title>'; $postData .= '</entry>'; - if ($dom = $this->sendRequest($feedUri, 'POST', $postData)) { + if ($dom = $this->sendRequest($uri, 'POST', $postData)) { return true; } } @@ -182,7 +210,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } $files = array(); while ($next) { - $dom = $this->sendRequest($next, 'GET'); + $dom = $this->getFeed($next, 'GET'); $links = $dom->getElementsByTagName('link'); foreach ($links as $link) { if ($link->getAttribute('rel') == 'next') { @@ -286,72 +314,170 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { $links = $entry->getElementsByTagName('link'); foreach ($links as $link) { if ($link->getAttribute('rel') == 'self') { - $feedUri = $link->getAttribute('href'); + $uri = $link->getAttribute('href'); + break; } } } - if (isset($feedUri)) { - $this->sendRequest($feedUri, 'DELETE'); + if (isset($uri)) { + $this->sendRequest($uri, 'DELETE'); return true; } return false; } public function rename($path1, $path2) { - // TODO Add support for moving to different collections - // Get resource edit link to rename resource if ($entry = $this->getResource($path1)) { - $etag = $entry->getElementsByTagName('entry')->item(0)->getAttribute('gd:etag'); - $links = $entry->getElementsByTagName('link'); - foreach ($links as $link) { - if ($link->getAttribute('rel') == 'edit') { - $feedUri = $link->getAttribute('href'); + $collection = dirname($path2); + if (dirname($path1) == $collection) { + // Get resource edit link to rename resource + $etag = $entry->getAttribute('gd:etag'); + $links = $entry->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'edit') { + $uri = $link->getAttribute('href'); + break; + } + } + $title = basename($path); + // Construct post data + $postData = '<?xml version="1.0" encoding="UTF-8"?>'; + $postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>'; + $postData .= '<title>'.$title.'</title>'; + $postData .= '</entry>'; + $this->sendRequest($uri, 'PUT', $postData); + return true; + } else { + // Move to different collection + if ($collectionEntry = $this->getResource($collection)) { + $feedUri = $colelctionEntry->getElementsByTagName('content')->item(0)->getAttribute('src'); + // Construct post data + $postData = '<?xml version="1.0" encoding="UTF-8"?>'; + $postData .= '<entry xmlns="http://www.w3.org/2005/Atom">'; + $postData .= '<id>'.$entry->getElementsByTagName('id')->item(0).'</id>'; + $postData .= '</entry>'; + $this->sendRequest($uri, 'POST', $postData); + return true; } } } - if (isset($etag) && isset($feedUri)) { - $title = basename($path2); - // Construct post data - $postData = '<?xml version="1.0" encoding="UTF-8"?>'; - $postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007" xmlns:gd="http://schemas.google.com/g/2005" gd:etag='.$etag.'>'; - $postData .= '<title>'.$title.'</title>'; - $postData .= '</entry>'; - $this->sendRequest($feedUri, 'PUT', $postData); - return true; - } return false; } public function fopen($path, $mode) { - if ($entry = $this->getResource($path)) { - switch ($mode) { - case 'r': - case 'rb': + switch ($mode) { + case 'r': + case 'rb': + if ($entry = $this->getResource($path)) { $extension = $this->getExtension($entry); $downloadUri = $entry->getElementsByTagName('content')->item(0)->getAttribute('src'); // TODO Non-native documents don't need these additional parameters $downloadUri .= '&exportFormat='.$extension.'&format='.$extension; - $tmpFile = $this->sendRequest($downloadUri, 'GET', true); + $tmpFile = $this->sendRequest($downloadUri, 'GET', null, null, true); return fopen($tmpFile, 'r'); - case 'w': - case 'wb': - case 'a': - case 'ab': - case 'r+': - case 'w+': - case 'wb+': - case 'a+': - case 'x': - case 'x+': - case 'c': - case 'c+': - // TODO Edit documents - } - + } + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + if (strrpos($path,'.') !== false) { + $ext = substr($path,strrpos($path,'.')); + } else { + $ext = ''; + } + $tmpFile = OC_Helper::tmpFile($ext); + OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + if ($this->file_exists($path)) { + $source = $this->fopen($path, 'r'); + file_put_contents($tmpFile, $source); + } + self::$tempFiles[$tmpFile] = $path; + return fopen('close://'.$tmpFile, $mode); } return false; } + public function writeBack($tmpFile) { + if (isset(self::$tempFiles[$tmpFile])) { + $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]); + unlink($tmpFile); + } + } + + private function uploadFile($path, $target) { + $entry = $this->getResource($target); + if (!$entry) { + if (dirname($target) == '.' || dirname($target) == '/') { + $uploadUri = 'https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents'; + } else { + $entry = $this->getResource(dirname($target)); + } + } + if (!isset($uploadUri) && $entry) { + $etag = $entry->getAttribute('gd:etag'); + $links = $entry->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->getAttribute('rel') == 'http://schemas.google.com/g/2005#resumable-edit-media') { + $uploadUri = $link->getAttribute('href'); + break; + } + } + } + if (isset($uploadUri) && $handle = fopen($path, 'r')) { + $uploadUri .= '?convert=false'; + $mimetype = OC_Helper::getMimeType($path); + $size = filesize($path); + $headers = array('X-Upload-Content-Type: ' => $mimetype, 'X-Upload-Content-Length: ' => $size); + $postData = '<?xml version="1.0" encoding="UTF-8"?>'; + $postData .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">'; + $postData .= '<title>'.basename($target).'</title>'; + $postData .= '</entry>'; + $result = $this->sendRequest($uploadUri, 'POST', $postData, $headers, false, true); + if ($result) { + // Get location to upload file + if (preg_match('@^Location: (.*)$@m', $result, $matches)) { + $uploadUri = trim($matches[1]); + } + } else { + return false; + } + // 512 kB chunks + $chunkSize = 524288; + $i = 0; + while (!feof($handle)) { + if ($i + $chunkSize > $size) { + if ($i == 0) { + $chunkSize = $size; + } else { + $chunkSize = $size % $i; + } + } + $end = $i + $chunkSize - 1; + $headers = array('Content-Length: '.$chunkSize, 'Content-Type: '.$mimetype, 'Content Range: bytes '.$i.'-'.$end.'/'.$size); + $postData = fread($handle, $chunkSize); + $result = $this->sendRequest($uploadUri, 'PUT', $postData, $headers, false, true, false); + if ($result) { + // Get next location to upload file chunk + if (preg_match('@^Location: (.*)$@m', $result, $matches)) { + $uploadUri = trim($matches[1]); + } + $i += $chunkSize; + } else { + return false; + } + } + // TODO Wait for resource entry + } + } + public function getMimeType($path, $entry = null) { // Entry can be passed, because extension is required for opendir and the entry can't be cached without the extension if ($entry == null) { @@ -393,7 +519,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } public function free_space($path) { - if ($dom = $this->sendRequest('https://docs.google.com/feeds/metadata/default', 'GET')) { + if ($dom = $this->getFeed('https://docs.google.com/feeds/metadata/default', 'GET')) { // NOTE: Native Google Docs resources don't count towards quota $total = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesTotal')->item(0)->nodeValue; $used = $dom->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue; diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php new file mode 100755 index 00000000000..e3ba9c240cf --- /dev/null +++ b/apps/files_external/lib/swift.php @@ -0,0 +1,531 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once('php-cloudfiles/cloudfiles.php'); + +class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ + private $host; + private $root; + private $user; + private $token; + private $secure; + /** + * @var CF_Authentication auth + */ + private $auth; + /** + * @var CF_Connection conn + */ + private $conn; + /** + * @var CF_Container rootContainer + */ + private $rootContainer; + + private static $tempFiles=array(); + private $objects=array(); + private $containers=array(); + + const SUBCONTAINER_FILE='.subcontainers'; + + /** + * translate directory path to container name + * @param string path + * @return string + */ + private function getContainerName($path){ + $path=trim($this->root.$path,'/'); + return str_replace('/','\\',$path); + } + + /** + * get container by path + * @param string path + * @return CF_Container + */ + private function getContainer($path){ + if($path=='' or $path=='/'){ + return $this->rootContainer; + } + if(isset($this->containers[$path])){ + return $this->containers[$path]; + } + try{ + $container=$this->conn->get_container($this->getContainerName($path)); + $this->containers[$path]=$container; + return $container; + }catch(NoSuchContainerException $e){ + return null; + } + } + + /** + * create container + * @param string path + * @return CF_Container + */ + private function createContainer($path){ + if($path=='' or $path=='/'){ + return $this->conn->create_container($this->getContainerName($path)); + } + $parent=dirname($path); + if($parent=='' or $parent=='/'){ + $parentContainer=$this->rootContainer; + }else{ + if(!$this->containerExists($parent)){ + $parentContainer=$this->createContainer($parent); + }else{ + $parentContainer=$this->getContainer($parent); + } + } + $this->addSubContainer($parentContainer,basename($path)); + return $this->conn->create_container($this->getContainerName($path)); + } + + /** + * get object by path + * @param string path + * @return CF_Object + */ + private function getObject($path){ + if(isset($this->objects[$path])){ + return $this->objects[$path]; + } + $container=$this->getContainer(dirname($path)); + if(is_null($container)){ + return null; + }else{ + try{ + $obj=$container->get_object(basename($path)); + $this->objects[$path]=$obj; + return $obj; + }catch(NoSuchObjectException $e){ + return null; + } + } + } + + /** + * get the names of all objects in a container + * @param CF_Container + * @return array + */ + private function getObjects($container){ + if(is_null($container)){ + return array(); + }else{ + $files=$container->get_objects(); + foreach($files as &$file){ + $file=$file->name; + } + return $files; + } + } + + /** + * create object + * @param string path + * @return CF_Object + */ + private function createObject($path){ + $container=$this->getContainer(dirname($path)); + if(!is_null($container)){ + $container=$this->createContainer($path); + } + return $container->create_object(basename($path)); + } + + /** + * check if an object exists + * @param string + * @return bool + */ + private function objectExists($path){ + return !is_null($this->getObject($path)); + } + + /** + * check if container for path exists + * @param string path + * @return bool + */ + private function containerExists($path){ + return !is_null($this->getContainer($path)); + } + + /** + * get the list of emulated sub containers + * @param CF_Container container + * @return array + */ + private function getSubContainers($container){ + $tmpFile=OCP\Files::tmpFile(); + $obj=$this->getSubContainerFile($container); + try{ + $obj->save_to_filename($tmpFile); + }catch(Exception $e){ + return array(); + } + $obj->save_to_filename($tmpFile); + $containers=file($tmpFile); + unlink($tmpFile); + foreach($containers as &$sub){ + $sub=trim($sub); + } + return $containers; + } + + /** + * add an emulated sub container + * @param CF_Container container + * @param string name + * @return bool + */ + private function addSubContainer($container,$name){ + if(!$name){ + return false; + } + $tmpFile=OCP\Files::tmpFile(); + $obj=$this->getSubContainerFile($container); + try{ + $obj->save_to_filename($tmpFile); + $containers=file($tmpFile); + foreach($containers as &$sub){ + $sub=trim($sub); + } + if(array_search($name,$containers)!==false){ + unlink($tmpFile); + return false; + }else{ + $fh=fopen($tmpFile,'a'); + fwrite($fh,$name."\n"); + } + }catch(Exception $e){ + $containers=array(); + file_put_contents($tmpFile,$name."\n"); + } + + $obj->load_from_filename($tmpFile); + unlink($tmpFile); + return true; + } + + /** + * remove an emulated sub container + * @param CF_Container container + * @param string name + * @return bool + */ + private function removeSubContainer($container,$name){ + if(!$name){ + return false; + } + $tmpFile=OCP\Files::tmpFile(); + $obj=$this->getSubContainerFile($container); + try{ + $obj->save_to_filename($tmpFile); + $containers=file($tmpFile); + }catch(Exception $e){ + return false; + } + foreach($containers as &$sub){ + $sub=trim($sub); + } + $i=array_search($name,$containers); + if($i===false){ + unlink($tmpFile); + return false; + }else{ + unset($containers[$i]); + file_put_contents($tmpFile,implode("\n",$containers)."\n"); + } + + $obj->load_from_filename($tmpFile); + unlink($tmpFile); + return true; + } + + /** + * ensure a subcontainer file exists and return it's object + * @param CF_Container container + * @return CF_Object + */ + private function getSubContainerFile($container){ + try{ + return $container->get_object(self::SUBCONTAINER_FILE); + }catch(NoSuchObjectException $e){ + return $container->create_object(self::SUBCONTAINER_FILE); + } + } + + public function __construct($params){ + $this->token=$params['token']; + $this->host=$params['host']; + $this->user=$params['user']; + $this->root=isset($params['root'])?$params['root']:'/'; + $this->secure=isset($params['secure'])?(bool)$params['secure']:true; + if(substr($this->root,0,1)!='/'){ + $this->root='/'.$this->root; + } + $this->auth = new CF_Authentication($this->user, $this->token, null, $this->host); + $this->auth->authenticate(); + + $this->conn = new CF_Connection($this->auth); + + if(!$this->containerExists($this->root)){ + $this->rootContainer=$this->createContainer('/'); + }else{ + $this->rootContainer=$this->getContainer('/'); + } + } + + + public function mkdir($path){ + if($this->containerExists($path)){ + return false; + }else{ + $this->createContainer($path); + return true; + } + } + + public function rmdir($path){ + if(!$this->containerExists($path)){ + return false; + }else{ + $this->emptyContainer($path); + if($path!='' and $path!='/'){ + $parentContainer=$this->getContainer(dirname($path)); + $this->removeSubContainer($parentContainer,basename($path)); + } + + $this->conn->delete_container($this->getContainerName($path)); + unset($this->containers[$path]); + return true; + } + } + + private function emptyContainer($path){ + $container=$this->getContainer($path); + if(is_null($container)){ + return; + } + $subContainers=$this->getSubContainers($container); + foreach($subContainers as $sub){ + if($sub){ + $this->emptyContainer($path.'/'.$sub); + $this->conn->delete_container($this->getContainerName($path.'/'.$sub)); + unset($this->containers[$path.'/'.$sub]); + } + } + + $objects=$this->getObjects($container); + foreach($objects as $object){ + $container->delete_object($object); + unset($this->objects[$path.'/'.$object]); + } + } + + public function opendir($path){ + $container=$this->getContainer($path); + $files=$this->getObjects($container); + $i=array_search(self::SUBCONTAINER_FILE,$files); + if($i!==false){ + unset($files[$i]); + } + $subContainers=$this->getSubContainers($container); + $files=array_merge($files,$subContainers); + $id=$this->getContainerName($path); + OC_FakeDirStream::$dirs[$id]=$files; + return opendir('fakedir://'.$id); + } + + public function filetype($path){ + if($this->containerExists($path)){ + return 'dir'; + }else{ + return 'file'; + } + } + + public function is_readable($path){ + return true; + } + + public function is_writable($path){ + return true; + } + + public function file_exists($path){ + if($this->is_dir($path)){ + return true; + }else{ + return $this->objectExists($path); + } + } + + public function file_get_contents($path){ + $obj=$this->getObject($path); + if(is_null($obj)){ + return false; + } + return $obj->read(); + } + + public function file_put_contents($path,$content){ + $obj=$this->getObject($path); + if(is_null($obj)){ + $container=$this->getContainer(dirname($path)); + if(is_null($container)){ + return false; + } + $obj=$container->create_object(basename($path)); + } + $this->resetMTime($obj); + return $obj->write($content); + } + + public function unlink($path){ + if($this->objectExists($path)){ + $container=$this->getContainer(dirname($path)); + $container->delete_object(basename($path)); + unset($this->objects[$path]); + }else{ + return false; + } + } + + public function fopen($path,$mode){ + $obj=$this->getObject($path); + if(is_null($obj)){ + return false; + } + switch($mode){ + case 'r': + case 'rb': + $fp = fopen('php://temp', 'r+'); + $obj->stream($fp); + + rewind($fp); + return $fp; + case 'w': + case 'wb': + case 'a': + case 'ab': + case 'r+': + case 'w+': + case 'wb+': + case 'a+': + case 'x': + case 'x+': + case 'c': + case 'c+': + $tmpFile=$this->getTmpFile($path); + OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + self::$tempFiles[$tmpFile]=$path; + return fopen('close://'.$tmpFile,$mode); + } + } + + public function writeBack($tmpFile){ + if(isset(self::$tempFiles[$tmpFile])){ + $this->fromTmpFile($tmpFile,self::$tempFiles[$tmpFile]); + unlink($tmpFile); + } + } + + public function free_space($path){ + return 0; + } + + public function touch($path,$mtime=null){ + $obj=$this->getObject($path); + if(is_null($obj)){ + return false; + } + if(is_null($mtime)){ + $mtime=time(); + } + + //emulate setting mtime with metadata + $obj->metadata['Mtime']=$mtime; + $obj->sync_metadata(); + } + + public function rename($path1,$path2){ + $sourceContainer=$this->getContainer(dirname($path1)); + $targetContainer=$this->getContainer(dirname($path2)); + $result=$sourceContainer->move_object_to(basename($path1),$targetContainer,basename($path2)); + unset($this->objects[$path1]); + if($result){ + $targetObj=$this->getObject($path2); + $this->resetMTime($targetObj); + } + return $result; + } + + public function copy($path1,$path2){ + $sourceContainer=$this->getContainer(dirname($path1)); + $targetContainer=$this->getContainer(dirname($path2)); + $result=$sourceContainer->copy_object_to(basename($path1),$targetContainer,basename($path2)); + if($result){ + $targetObj=$this->getObject($path2); + $this->resetMTime($targetObj); + } + return $result; + } + + public function stat($path){ + $obj=$this->getObject($path); + if(is_null($obj)){ + return false; + } + + if(isset($obj->metadata['Mtime']) and $obj->metadata['Mtime']>-1){ + $mtime=$obj->metadata['Mtime']; + }else{ + $mtime=strtotime($obj->last_modified); + } + return array( + 'mtime'=>$mtime, + 'size'=>$obj->content_length, + 'ctime'=>-1, + ); + } + + private function getTmpFile($path){ + $obj=$this->getObject($path); + if(!is_null($obj)){ + $tmpFile=OCP\Files::tmpFile(); + $obj->save_to_filename($tmpFile); + return $tmpFile; + }else{ + return false; + } + } + + private function fromTmpFile($tmpFile,$path){ + $obj=$this->getObject($path); + if(is_null($obj)){ + $obj=$this->createObject($path); + } + $obj->load_from_filename($tmpFile); + $this->resetMTime($obj); + } + + /** + * remove custom mtime metadata + * @param CF_Object obj + */ + private function resetMTime($obj){ + if(isset($obj->metadata['Mtime'])){ + $obj->metadata['Mtime']=-1; + $obj->sync_metadata(); + } + } +} diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 7a2da5c8ec0..07c90d4878e 100644..100755 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -150,7 +150,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ }else{ $ext=''; } - $tmpFile=OC_Helper::tmpFile($ext); + $tmpFile=OCP\Files::tmpFile($ext); OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)){ $this->getFile($path,$tmpFile); diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index fa4c74a6e26..5b6517755f2 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -21,5 +21,12 @@ return array( 'token'=>'test', 'token_secret'=>'test', 'root'=>'/google', - ) + ), + 'swift'=>array( + 'run'=>false, + 'user'=>'test:tester', + 'token'=>'testing', + 'host'=>'localhost:8080/auth', + 'root'=>'/', + ), ); diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index e30fb9a1c38..68481b4e66b 100644..100755 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -23,7 +23,7 @@ if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']){ } public function tearDown(){ - OC_Helper::rmdirr($this->instance->constructUrl('')); + OCP\Files::rmdirr($this->instance->constructUrl('')); } } } diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php new file mode 100644 index 00000000000..f0bde6ed605 --- /dev/null +++ b/apps/files_external/tests/swift.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$config=include('apps/files_external/tests/config.php'); +if(!is_array($config) or !isset($config['swift']) or !$config['swift']['run']){ + abstract class Test_Filestorage_SWIFT extends Test_FileStorage{} + return; +}else{ + class Test_Filestorage_SWIFT extends Test_FileStorage { + private $config; + private $id; + + public function setUp(){ + $id=uniqid(); + $this->config=include('apps/files_external/tests/config.php'); + $this->config['swift']['root'].='/'.$id;//make sure we have an new empty folder to work in + $this->instance=new OC_Filestorage_SWIFT($this->config['swift']); + } + + + public function tearDown(){ + $this->instance->rmdir(''); + } + + } +} + |