summaryrefslogtreecommitdiffstats
path: root/apps/files_external
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_external')
-rw-r--r--apps/files_external/appinfo/app.php11
-rw-r--r--apps/files_external/appinfo/info.xml13
-rw-r--r--apps/files_external/lib/ftp.php157
-rw-r--r--apps/files_external/lib/google.php409
-rw-r--r--apps/files_external/lib/webdav.php293
-rw-r--r--apps/files_external/tests/config.php22
-rw-r--r--apps/files_external/tests/ftp.php23
-rw-r--r--apps/files_external/tests/google.php38
-rw-r--r--apps/files_external/tests/webdav.php23
9 files changed, 989 insertions, 0 deletions
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php
new file mode 100644
index 00000000000..95770b44b75
--- /dev/null
+++ b/apps/files_external/appinfo/app.php
@@ -0,0 +1,11 @@
+<?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.
+ */
+
+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';
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
new file mode 100644
index 00000000000..fb58297ff17
--- /dev/null
+++ b/apps/files_external/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+ <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>
+ <types>
+ <filesystem/>
+ </types>
+</info>
diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php
new file mode 100644
index 00000000000..802446b4fd8
--- /dev/null
+++ b/apps/files_external/lib/ftp.php
@@ -0,0 +1,157 @@
+<?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.
+ */
+
+class OC_FileStorage_FTP extends OC_Filestorage_Common{
+ private $password;
+ private $user;
+ private $host;
+ private $secure;
+ private $root;
+
+ private static $tempFiles=array();
+
+ public function __construct($params){
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ $this->secure=isset($params['secure'])?(bool)$params['secure']:false;
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if(substr($this->root,0,1)!='/'){
+ $this->root='/'.$this->root;
+ }
+
+ //create the root folder if necesary
+ mkdir($this->constructUrl(''));
+ }
+
+ /**
+ * construct the ftp url
+ * @param string path
+ * @return string
+ */
+ public function constructUrl($path){
+ $url='ftp';
+ if($this->secure){
+ $url.='s';
+ }
+ $url.='://'.$this->user.':'.$this->password.'@'.$this->host.$this->root.$path;
+ return $url;
+ }
+
+ public function mkdir($path){
+ return mkdir($this->constructUrl($path));
+ }
+
+ public function rmdir($path){
+ if($this->file_exists($path)){
+ $succes=rmdir($this->constructUrl($path));
+ clearstatcache();
+ return $succes;
+ }else{
+ return false;
+ }
+ }
+
+ public function opendir($path){
+ return opendir($this->constructUrl($path));
+ }
+
+ public function filetype($path){
+ return filetype($this->constructUrl($path));
+ }
+
+ public function is_readable($path){
+ return true;//not properly supported
+ }
+
+ public function is_writable($path){
+ return true;//not properly supported
+ }
+
+ public function file_exists($path){
+ return file_exists($this->constructUrl($path));
+ }
+
+ public function unlink($path){
+ $succes=unlink($this->constructUrl($path));
+ clearstatcache();
+ return $succes;
+ }
+
+ public function fopen($path,$mode){
+ switch($mode){
+ case 'r':
+ case 'rb':
+ case 'w':
+ case 'wb':
+ case 'a':
+ case 'ab':
+ //these are supported by the wrapper
+ $context = stream_context_create(array('ftp' => array('overwrite' => true)));
+ return fopen($this->constructUrl($path),$mode,false,$context);
+ case 'r+':
+ case 'w+':
+ case 'wb+':
+ case 'a+':
+ case 'x':
+ case 'x+':
+ case 'c':
+ case 'c+':
+ //emulate these
+ 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)){
+ $this->getFile($path,$tmpFile);
+ }
+ self::$tempFiles[$tmpFile]=$path;
+ return fopen('close://'.$tmpFile,$mode);
+ }
+ }
+
+ public function writeBack($tmpFile){
+ if(isset(self::$tempFiles[$tmpFile])){
+ $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]);
+ unlink($tmpFile);
+ }
+ }
+
+ public function free_space($path){
+ return 0;
+ }
+
+ public function touch($path,$mtime=null){
+ if(is_null($mtime)){
+ $fh=$this->fopen($path,'a');
+ fwrite($fh,'');
+ fclose($fh);
+ }else{
+ return false;//not supported
+ }
+ }
+
+ public function getFile($path,$target){
+ return copy($this->constructUrl($path),$target);
+ }
+
+ public function uploadFile($path,$target){
+ return copy($path,$this->constructUrl($target));
+ }
+
+ public function rename($path1,$path2){
+ return rename($this->constructUrl($path1),$this->constructUrl($path2));
+ }
+
+ public function stat($path){
+ return stat($this->constructUrl($path));
+ }
+}
diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php
new file mode 100644
index 00000000000..0d6db1987f8
--- /dev/null
+++ b/apps/files_external/lib/google.php
@@ -0,0 +1,409 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* 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/>.
+*/
+
+require_once 'common.inc.php';
+
+class OC_Filestorage_Google extends OC_Filestorage_Common {
+
+ private $consumer;
+ private $oauth_token;
+ private $sig_method;
+ private $entries;
+
+ public function __construct($arguments) {
+ $consumer_key = isset($arguments['consumer_key']) ? $arguments['consumer_key'] : 'anonymous';
+ $consumer_secret = isset($arguments['consumer_secret']) ? $arguments['consumer_secret'] : 'anonymous';
+ $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
+ $this->oauth_token = new OAuthToken($arguments['token'], $arguments['token_secret']);
+ $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1();
+ $this->entries = array();
+ }
+
+ private function sendRequest($feedUri, $http_method, $isDownload = false, $postData = null) {
+ $feedUri = trim($feedUri);
+ // create an associative array from each key/value url query param pair.
+ $params = array();
+ $pieces = explode('?', $feedUri);
+ if (isset($pieces[1])) {
+ $params = explode_assoc('=', '&', $pieces[1]);
+ }
+ // urlencode each url parameter key/value pair
+ $tempStr = $pieces[0];
+ 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);
+ $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);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_FAILONERROR, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ switch ($http_method) {
+ case 'GET':
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ break;
+ case 'POST':
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
+ break;
+ case 'PUT':
+ $headers[] = 'If-Match: *';
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
+ 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);
+ 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;
+ }
+ $result = curl_exec($curl);
+ curl_close($curl);
+ $dom = new DOMDocument();
+ $dom->loadXML($result);
+ return $dom;
+ }
+
+ private function getResource($path) {
+ $file = basename($path);
+ if (array_key_exists($file, $this->entries)) {
+ return $this->entries[$file];
+ } else {
+ // 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');
+ // 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');
+ // Check if request was successful and entry exists
+ if ($dom && $entry = $dom->getElementsByTagName('entry')->item(0)) {
+ $this->entries[$file] = $entry;
+ return $entry;
+ }
+ return false;
+ }
+ }
+
+ private function getExtension($entry) {
+ $mimetype = $this->getMimeType('', $entry);
+ switch ($mimetype) {
+ case 'httpd/unix-directory':
+ return '';
+ case 'application/vnd.oasis.opendocument.text':
+ return 'odt';
+ case 'application/vnd.oasis.opendocument.spreadsheet':
+ return 'ods';
+ case 'application/vnd.oasis.opendocument.presentation':
+ return 'pptx';
+ case 'text/html':
+ return 'html';
+ default:
+ return 'html';
+ }
+ }
+
+
+ public function mkdir($path) {
+ $dir = dirname($path);
+ // Check if path parent is root directory
+ if ($dir == '/' || $dir == '\.' || $dir == '.') {
+ $feedUri = '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');
+ }
+ if (isset($feedUri)) {
+ $title = basename($path);
+ // Construct post data
+ $postData = '<?xml version="1.0" encoding="UTF-8"?>';
+ $postData .= '<entry xmlns="http://www.w3.org/2005/Atom">';
+ $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)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function rmdir($path) {
+ return $this->unlink($path);
+ }
+
+ public function opendir($path) {
+ if ($path == '' || $path == '/') {
+ $next = 'https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents';
+ } else {
+ if ($entry = $this->getResource($path)) {
+ $next = $entry->getElementsByTagName('content')->item(0)->getAttribute('src');
+ } else {
+ return false;
+ }
+ }
+ $files = array();
+ while ($next) {
+ $dom = $this->sendRequest($next, 'GET');
+ $links = $dom->getElementsByTagName('link');
+ foreach ($links as $link) {
+ if ($link->getAttribute('rel') == 'next') {
+ $next = $link->getAttribute('src');
+ break;
+ } else {
+ $next = false;
+ }
+ }
+ $entries = $dom->getElementsByTagName('entry');
+ foreach ($entries as $entry) {
+ $name = $entry->getElementsByTagName('title')->item(0)->nodeValue;
+ // Google Docs resources don't always include extensions in title
+ if (!strpos($name, '.')) {
+ $extension = $this->getExtension($entry);
+ if ($extension != '') {
+ $name .= '.'.$extension;
+ }
+ }
+ $files[] = $name;
+ // Cache entry for future use
+ $this->entries[$name] = $entry;
+ }
+ }
+ OC_FakeDirStream::$dirs['google'] = $files;
+ return opendir('fakedir://google');
+ }
+
+ public function stat($path) {
+ if ($path == '' || $path == '/') {
+ $stat['size'] = $this->free_space($path);
+ $stat['atime'] = time();
+ $stat['mtime'] = time();
+ $stat['ctime'] = time();
+ } else if ($entry = $this->getResource($path)) {
+ // NOTE: Native resources don't have a file size
+ $stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue;
+// if (isset($atime = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue))
+// $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue);
+ $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue);
+ $stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue);
+ }
+ if (isset($stat)) {
+ return $stat;
+ }
+ return false;
+ }
+
+ public function filetype($path) {
+ if ($path == '' || $path == '/') {
+ return 'dir';
+ } else if ($entry = $this->getResource($path)) {
+ $categories = $entry->getElementsByTagName('category');
+ foreach ($categories as $category) {
+ if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
+ $type = $category->getAttribute('label');
+ if (strlen(strstr($type, 'folder')) > 0) {
+ return 'dir';
+ } else {
+ return 'file';
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public function is_readable($path) {
+ return true;
+ }
+
+ public function is_writable($path) {
+ if ($path == '' || $path == '/') {
+ return true;
+ } else if ($entry = $this->getResource($path)) {
+ // Check if edit or edit-media links exist
+ $links = $entry->getElementsByTagName('link');
+ foreach ($links as $link) {
+ if ($link->getAttribute('rel') == 'edit') {
+ return true;
+ } else if ($link->getAttribute('rel') == 'edit-media') {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public function file_exists($path) {
+ if ($path == '' || $path == '/') {
+ return true;
+ } else if ($this->getResource($path)) {
+ return true;
+ }
+ return false;
+ }
+
+ public function unlink($path) {
+ // Get resource self link to trash resource
+ if ($entry = $this->getResource($path)) {
+ $links = $entry->getElementsByTagName('link');
+ foreach ($links as $link) {
+ if ($link->getAttribute('rel') == 'self') {
+ $feedUri = $link->getAttribute('href');
+ }
+ }
+ }
+ if (isset($feedUri)) {
+ $this->sendRequest($feedUri, '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');
+ }
+ }
+ }
+ 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':
+ $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);
+ 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
+ }
+
+ }
+ return false;
+ }
+
+ 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) {
+ if ($path == '' || $path == '/') {
+ return 'httpd/unix-directory';
+ } else {
+ $entry = $this->getResource($path);
+ }
+ }
+ if ($entry) {
+ $mimetype = $entry->getElementsByTagName('content')->item(0)->getAttribute('type');
+ // Native Google Docs resources often default to text/html, but it may be more useful to default to a corresponding ODF mimetype
+ // Collections get reported as application/atom+xml, make sure it actually is a folder and fix the mimetype
+ if ($mimetype == 'text/html' || $mimetype == 'application/atom+xml;type=feed') {
+ $categories = $entry->getElementsByTagName('category');
+ foreach ($categories as $category) {
+ if ($category->getAttribute('scheme') == 'http://schemas.google.com/g/2005#kind') {
+ $type = $category->getAttribute('label');
+ if (strlen(strstr($type, 'folder')) > 0) {
+ return 'httpd/unix-directory';
+ } else if (strlen(strstr($type, 'document')) > 0) {
+ return 'application/vnd.oasis.opendocument.text';
+ } else if (strlen(strstr($type, 'spreadsheet')) > 0) {
+ return 'application/vnd.oasis.opendocument.spreadsheet';
+ } else if (strlen(strstr($type, 'presentation')) > 0) {
+ return 'application/vnd.oasis.opendocument.presentation';
+ } else if (strlen(strstr($type, 'drawing')) > 0) {
+ return 'application/vnd.oasis.opendocument.graphics';
+ } else {
+ // If nothing matches return text/html, all native Google Docs resources can be exported as text/html
+ return 'text/html';
+ }
+ }
+ }
+ }
+ return $mimetype;
+ }
+ return false;
+ }
+
+ public function free_space($path) {
+ if ($dom = $this->sendRequest('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;
+ return $total - $used;
+ }
+ return false;
+ }
+
+ public function touch($path, $mtime = null) {
+
+ }
+
+} \ No newline at end of file
diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php
new file mode 100644
index 00000000000..7a2da5c8ec0
--- /dev/null
+++ b/apps/files_external/lib/webdav.php
@@ -0,0 +1,293 @@
+<?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.
+ */
+
+class OC_FileStorage_DAV extends OC_Filestorage_Common{
+ private $password;
+ private $user;
+ private $host;
+ private $secure;
+ private $root;
+ /**
+ * @var Sabre_DAV_Client
+ */
+ private $client;
+
+ private static $tempFiles=array();
+
+ public function __construct($params){
+ $this->host=$params['host'];
+ $this->user=$params['user'];
+ $this->password=$params['password'];
+ $this->secure=isset($params['secure'])?(bool)$params['secure']:false;
+ $this->root=isset($params['root'])?$params['root']:'/';
+ if(substr($this->root,0,1)!='/'){
+ $this->root='/'.$this->root;
+ }
+ if(substr($this->root,-1,1)!='/'){
+ $this->root.='/';
+ }
+
+ $settings = array(
+ 'baseUri' => $this->createBaseUri(),
+ 'userName' => $this->user,
+ 'password' => $this->password,
+ );
+ $this->client = new Sabre_DAV_Client($settings);
+
+ //create the root folder if necesary
+ $this->mkdir('');
+ }
+
+ private function createBaseUri(){
+ $baseUri='http';
+ if($this->secure){
+ $baseUri.'s';
+ }
+ $baseUri.='://'.$this->host.$this->root;
+ return $baseUri;
+ }
+
+ public function mkdir($path){
+ $path=$this->cleanPath($path);
+ return $this->simpleResponse('MKCOL',$path,null,201);
+ }
+
+ public function rmdir($path){
+ $path=$this->cleanPath($path);
+ return $this->simpleResponse('DELETE',$path,null,204);
+ }
+
+ public function opendir($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array(),1);
+ $stripLength=strlen($this->root)+strlen($path);
+ $id=md5('webdav'.$this->root.$path);
+ OC_FakeDirStream::$dirs[$id]=array();
+ foreach($response as $file=>$data){
+ //strip root and path
+ $file=trim(substr($file,$stripLength));
+ $file=trim($file,'/');
+ if($file){
+ OC_FakeDirStream::$dirs[$id][]=$file;
+ }
+ }
+ return opendir('fakedir://'.$id);
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function filetype($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}resourcetype'));
+ $responseType=$response["{DAV:}resourcetype"]->resourceType;
+ return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function is_readable($path){
+ return true;//not properly supported
+ }
+
+ public function is_writable($path){
+ return true;//not properly supported
+ }
+
+ public function file_exists($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}resourcetype'));
+ return true;//no 404 exception
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ public function unlink($path){
+ return $this->simpleResponse('DELETE',$path,null,204);
+ }
+
+ public function fopen($path,$mode){
+ $path=$this->cleanPath($path);
+ switch($mode){
+ case 'r':
+ case 'rb':
+ //straight up curl instead of sabredav here, sabredav put's the entire get result in memory
+ $curl = curl_init();
+ $fp = fopen('php://temp', 'r+');
+ curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$path);
+ curl_setopt($curl, CURLOPT_FILE, $fp);
+
+ curl_exec ($curl);
+ curl_close ($curl);
+ 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+':
+ //emulate these
+ 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)){
+ $this->getFile($path,$tmpFile);
+ }
+ self::$tempFiles[$tmpFile]=$path;
+ return fopen('close://'.$tmpFile,$mode);
+ }
+ }
+
+ public function writeBack($tmpFile){
+ if(isset(self::$tempFiles[$tmpFile])){
+ $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]);
+ unlink($tmpFile);
+ }
+ }
+
+ public function free_space($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}quota-available-bytes'));
+ if(isset($response['{DAV:}quota-available-bytes'])){
+ return (int)$response['{DAV:}quota-available-bytes'];
+ }else{
+ return 0;
+ }
+ }catch(Exception $e){
+ return 0;
+ }
+ }
+
+ public function touch($path,$mtime=null){
+ if(is_null($mtime)){
+ $mtime=time();
+ }
+ $path=$this->cleanPath($path);
+ $this->client->proppatch($path, array('{DAV:}lastmodified' => $mtime,));
+ }
+
+ public function getFile($path,$target){
+ $source=$this->fopen($path,'r');
+ file_put_contents($target,$source);
+ }
+
+ public function uploadFile($path,$target){
+ $source=fopen($path,'r');
+
+ $curl = curl_init();
+ curl_setopt($curl,CURLOPT_USERPWD,$this->user.':'.$this->password);
+ curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().$target);
+ curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
+ curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer
+ curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path));
+ curl_setopt($curl, CURLOPT_PUT, true);
+ curl_exec ($curl);
+ curl_close ($curl);
+ }
+
+ public function rename($path1,$path2){
+ $path1=$this->cleanPath($path1);
+ $path2=$this->root.$this->cleanPath($path2);
+ try{
+ $response=$this->client->request('MOVE',$path1,null,array('Destination'=>$path2));
+ return true;
+ }catch(Exception $e){
+ echo $e;
+ echo 'fail';
+ var_dump($response);
+ return false;
+ }
+ }
+
+ public function copy($path1,$path2){
+ $path1=$this->cleanPath($path1);
+ $path2=$this->root.$this->cleanPath($path2);
+ try{
+ $response=$this->client->request('COPY',$path1,null,array('Destination'=>$path2));
+ return true;
+ }catch(Exception $e){
+ echo $e;
+ echo 'fail';
+ var_dump($response);
+ return false;
+ }
+ }
+
+ public function stat($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}getlastmodified','{DAV:}getcontentlength'));
+ if(isset($response['{DAV:}getlastmodified']) and isset($response['{DAV:}getcontentlength'])){
+ return array(
+ 'mtime'=>strtotime($response['{DAV:}getlastmodified']),
+ 'size'=>(int)$response['{DAV:}getcontentlength'],
+ 'ctime'=>-1,
+ );
+ }else{
+ return array();
+ }
+ }catch(Exception $e){
+ return array();
+ }
+ }
+
+ public function getMimeType($path){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->propfind($path, array('{DAV:}getcontenttype','{DAV:}resourcetype'));
+ $responseType=$response["{DAV:}resourcetype"]->resourceType;
+ $type=(count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file';
+ if($type=='dir'){
+ return 'httpd/unix-directory';
+ }elseif(isset($response['{DAV:}getcontenttype'])){
+ return $response['{DAV:}getcontenttype'];
+ }else{
+ return false;
+ }
+ }catch(Exception $e){
+ return false;
+ }
+ }
+
+ private function cleanPath($path){
+ if(substr($path,0,1)=='/'){
+ return substr($path,1);
+ }else{
+ return $path;
+ }
+ }
+
+ private function simpleResponse($method,$path,$body,$expected){
+ $path=$this->cleanPath($path);
+ try{
+ $response=$this->client->request($method,$path,$body);
+ return $response['statusCode']==$expected;
+ }catch(Exception $e){
+ return false;
+ }
+ }
+}
+
diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php
new file mode 100644
index 00000000000..9b40d2b98cf
--- /dev/null
+++ b/apps/files_external/tests/config.php
@@ -0,0 +1,22 @@
+<?php
+return array(
+ 'ftp'=>array(
+ 'host'=>'localhost',
+ 'user'=>'test',
+ 'password'=>'test',
+ 'root'=>'/test',
+ ),
+ 'webdav'=>array(
+ 'host'=>'localhost',
+ 'user'=>'test',
+ 'password'=>'test',
+ 'root'=>'/owncloud/files/webdav.php',
+ ),
+ 'google'=>array(
+ 'consumer_key'=>'anonymous',
+ 'consumer_secret'=>'anonymous',
+ 'token'=>'test',
+ 'token_secret'=>'test',
+ 'root'=>'/google',
+ )
+);
diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php
new file mode 100644
index 00000000000..aa565751ba3
--- /dev/null
+++ b/apps/files_external/tests/ftp.php
@@ -0,0 +1,23 @@
+<?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.
+ */
+
+class Test_Filestorage_FTP extends Test_FileStorage {
+ private $config;
+ private $id;
+
+ public function setUp(){
+ $id=uniqid();
+ $this->config=include('apps/files_external/tests/config.php');
+ $this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in
+ $this->instance=new OC_Filestorage_FTP($this->config['ftp']);
+ }
+
+ public function tearDown(){
+ OC_Helper::rmdirr($this->instance->constructUrl(''));
+ }
+}
diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php
new file mode 100644
index 00000000000..1c028945228
--- /dev/null
+++ b/apps/files_external/tests/google.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Michael Gapczynski
+* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
+*
+* 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 Test_Filestorage_Google extends Test_FileStorage {
+
+ private $config;
+ private $id;
+
+ public function setUp(){
+ $id=uniqid();
+ $this->config=include('apps/files_external/tests/config.php');
+ $this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in
+ $this->instance=new OC_Filestorage_Google($this->config['google']);
+ }
+
+ public function tearDown(){
+ $this->instance->rmdir('/');
+ }
+}
diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php
new file mode 100644
index 00000000000..51799290540
--- /dev/null
+++ b/apps/files_external/tests/webdav.php
@@ -0,0 +1,23 @@
+<?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.
+ */
+
+class Test_Filestorage_DAV extends Test_FileStorage {
+ private $config;
+ private $id;
+
+ public function setUp(){
+ $id=uniqid();
+ $this->config=include('apps/files_external/tests/config.php');
+ $this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in
+ $this->instance=new OC_Filestorage_DAV($this->config['webdav']);
+ }
+
+ public function tearDown(){
+ $this->instance->rmdir('/');
+ }
+}