server/apps/files_external/lib/webdav.php

390 lines
11 KiB
PHP
Raw Normal View History

2012-03-23 17:54:38 +00:00
<?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.
*/
2012-09-07 16:30:48 +00:00
namespace OC\Files\Storage;
class DAV extends \OC\Files\Storage\Common {
2012-03-23 17:54:38 +00:00
private $password;
private $user;
private $host;
private $secure;
private $root;
private $certPath;
private $ready;
2012-03-23 17:54:38 +00:00
/**
* @var \Sabre_DAV_Client
2012-03-23 17:54:38 +00:00
*/
private $client;
private static $tempFiles = array();
2012-08-29 06:42:49 +00:00
2012-09-07 13:22:01 +00:00
public function __construct($params) {
if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
$host = $params['host'];
//remove leading http[s], will be generated in createBaseUri()
if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
$this->host = $host;
$this->user = $params['user'];
$this->password = $params['password'];
if (isset($params['secure'])) {
if (is_string($params['secure'])) {
$this->secure = ($params['secure'] === 'true');
} else {
$this->secure = (bool)$params['secure'];
}
} else {
$this->secure = false;
}
if ($this->secure === true) {
$certPath = \OC_User::getHome(\OC_User::getUser()) . '/files_external/rootcerts.crt';
if (file_exists($certPath)) {
$this->certPath = $certPath;
}
}
$this->root = isset($params['root']) ? $params['root'] : '/';
if (!$this->root || $this->root[0] != '/') {
$this->root = '/' . $this->root;
}
if (substr($this->root, -1, 1) != '/') {
$this->root .= '/';
}
} else {
throw new \Exception();
2012-03-23 17:54:38 +00:00
}
}
private function init() {
if ($this->ready) {
return;
}
$this->ready = true;
2012-08-29 06:42:49 +00:00
$settings = array(
'baseUri' => $this->createBaseUri(),
'userName' => $this->user,
'password' => $this->password,
);
2012-03-23 17:54:38 +00:00
2013-01-15 13:57:23 +00:00
$this->client = new \Sabre_DAV_Client($settings);
2012-08-29 06:42:49 +00:00
if ($this->secure === true && $this->certPath) {
$this->client->addTrustedCertificates($this->certPath);
2012-07-09 08:19:19 +00:00
}
2012-03-23 17:54:38 +00:00
}
public function getId() {
return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
2012-03-23 17:54:38 +00:00
}
protected function createBaseUri() {
$baseUri = 'http';
if ($this->secure) {
$baseUri .= 's';
2012-03-23 17:54:38 +00:00
}
$baseUri .= '://' . $this->host . $this->root;
2012-03-23 17:54:38 +00:00
return $baseUri;
}
2012-09-07 13:22:01 +00:00
public function mkdir($path) {
$this->init();
$path = $this->cleanPath($path);
2012-10-28 22:58:08 +00:00
return $this->simpleResponse('MKCOL', $path, null, 201);
2012-03-23 17:54:38 +00:00
}
2012-09-07 13:22:01 +00:00
public function rmdir($path) {
$this->init();
$path = $this->cleanPath($path) . '/';
// FIXME: some WebDAV impl return 403 when trying to DELETE
// a non-empty folder
2012-10-28 22:58:08 +00:00
return $this->simpleResponse('DELETE', $path, null, 204);
2012-03-23 17:54:38 +00:00
}
2012-09-07 13:22:01 +00:00
public function opendir($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$response = $this->client->propfind($this->encodePath($path), array(), 1);
$id = md5('webdav' . $this->root . $path);
$content = array();
$files = array_keys($response);
array_shift($files); //the first entry is the current directory
foreach ($files as $file) {
$file = urldecode(basename($file));
$content[] = $file;
2012-03-23 17:54:38 +00:00
}
\OC\Files\Stream\Dir::register($id, $content);
return opendir('fakedir://' . $id);
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
2012-09-07 13:22:01 +00:00
public function filetype($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$response = $this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype'));
$responseType = array();
if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType;
}
return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
} catch (\Exception $e) {
error_log($e->getMessage());
\OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR);
2012-03-23 17:54:38 +00:00
return false;
}
}
2012-09-07 13:22:01 +00:00
public function file_exists($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype'));
return true; //no 404 exception
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
2012-09-07 13:22:01 +00:00
public function unlink($path) {
$this->init();
return $this->simpleResponse('DELETE', $path, null, 204);
2012-03-23 17:54:38 +00:00
}
public function fopen($path, $mode) {
$this->init();
$path = $this->cleanPath($path);
switch ($mode) {
2012-03-23 17:54:38 +00:00
case 'r':
case 'rb':
if (!$this->file_exists($path)) {
return false;
}
2012-03-23 17:54:38 +00:00
//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() . $this->encodePath($path));
2012-03-23 17:54:38 +00:00
curl_setopt($curl, CURLOPT_FILE, $fp);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
if ($this->secure === true) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
if ($this->certPath) {
curl_setopt($curl, CURLOPT_CAINFO, $this->certPath);
}
}
curl_exec($curl);
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($statusCode !== 200) {
\OCP\Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, \OCP\Util::ERROR);
}
curl_close($curl);
2012-03-23 17:54:38 +00:00
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 = '';
2012-03-23 17:54:38 +00:00
}
if ($this->file_exists($path)) {
$tmpFile = $this->getCachedFile($path);
} else {
$tmpFile = \OCP\Files::tmpFile($ext);
2012-03-23 17:54:38 +00:00
}
\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
self::$tempFiles[$tmpFile] = $path;
return fopen('close://' . $tmpFile, $mode);
2012-03-23 17:54:38 +00:00
}
}
2012-09-07 13:22:01 +00:00
public function writeBack($tmpFile) {
if (isset(self::$tempFiles[$tmpFile])) {
2012-10-23 20:53:54 +00:00
$this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
2012-03-23 17:54:38 +00:00
unlink($tmpFile);
}
}
2012-09-07 13:22:01 +00:00
public function free_space($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$response = $this->client->propfind($this->encodePath($path), array('{DAV:}quota-available-bytes'));
if (isset($response['{DAV:}quota-available-bytes'])) {
2012-03-23 17:54:38 +00:00
return (int)$response['{DAV:}quota-available-bytes'];
} else {
return \OC\Files\SPACE_UNKNOWN;
2012-03-23 17:54:38 +00:00
}
} catch (\Exception $e) {
2013-07-25 14:14:46 +00:00
return \OC\Files\SPACE_UNKNOWN;
2012-03-23 17:54:38 +00:00
}
}
public function touch($path, $mtime = null) {
$this->init();
if (is_null($mtime)) {
$mtime = time();
2012-03-23 17:54:38 +00:00
}
$path = $this->cleanPath($path);
// if file exists, update the mtime, else create a new empty file
if ($this->file_exists($path)) {
try {
$this->client->proppatch($this->encodePath($path), array('{DAV:}lastmodified' => $mtime));
} catch (\Sabre_DAV_Exception_NotImplemented $e) {
return false;
}
} else {
$this->file_put_contents($path, '');
}
return true;
2012-03-23 17:54:38 +00:00
}
protected function uploadFile($path, $target) {
$this->init();
$source = fopen($path, 'r');
2012-03-23 17:54:38 +00:00
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERPWD, $this->user . ':' . $this->password);
curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . str_replace(' ', '%20', $target));
2012-03-23 17:54:38 +00:00
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);
if ($this->secure === true) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
if ($this->certPath) {
curl_setopt($curl, CURLOPT_CAINFO, $this->certPath);
}
}
curl_exec($curl);
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($statusCode !== 200) {
\OCP\Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, \OCP\Util::ERROR);
}
curl_close($curl);
$this->removeCachedFile($target);
2012-03-23 17:54:38 +00:00
}
public function rename($path1, $path2) {
$this->init();
$path1 = $this->encodePath($this->cleanPath($path1));
$path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2));
try {
$this->client->request('MOVE', $path1, null, array('Destination' => $path2));
$this->removeCachedFile($path1);
$this->removeCachedFile($path2);
2012-03-23 17:54:38 +00:00
return true;
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
public function copy($path1, $path2) {
$this->init();
$path1 = $this->encodePath($this->cleanPath($path1));
$path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2));
try {
$this->client->request('COPY', $path1, null, array('Destination' => $path2));
$this->removeCachedFile($path2);
2012-03-23 17:54:38 +00:00
return true;
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
2012-09-07 13:22:01 +00:00
public function stat($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$response = $this->client->propfind($this->encodePath($path), array('{DAV:}getlastmodified', '{DAV:}getcontentlength'));
return array(
'mtime' => strtotime($response['{DAV:}getlastmodified']),
'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
);
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return array();
}
}
2012-09-07 13:22:01 +00:00
public function getMimeType($path) {
$this->init();
$path = $this->cleanPath($path);
try {
$response = $this->client->propfind($this->encodePath($path), array('{DAV:}getcontenttype', '{DAV:}resourcetype'));
$responseType = array();
if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType;
}
$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
if ($type == 'dir') {
2012-03-23 17:54:38 +00:00
return 'httpd/unix-directory';
} elseif (isset($response['{DAV:}getcontenttype'])) {
2012-03-23 17:54:38 +00:00
return $response['{DAV:}getcontenttype'];
} else {
2012-03-23 17:54:38 +00:00
return false;
}
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
/**
* @param string $path
*/
2013-02-12 10:05:12 +00:00
public function cleanPath($path) {
$path = \OC\Files\Filesystem::normalizePath($path);
// remove leading slash
return substr($path, 1);
2012-03-23 17:54:38 +00:00
}
/**
* URL encodes the given path but keeps the slashes
*
* @param string $path to encode
* @return string encoded path
*/
private function encodePath($path) {
// slashes need to stay
return str_replace('%2F', '/', rawurlencode($path));
}
/**
* @param string $method
* @param string $path
* @param integer $expected
*/
private function simpleResponse($method, $path, $body, $expected) {
$path = $this->cleanPath($path);
try {
$response = $this->client->request($method, $this->encodePath($path), $body);
return $response['statusCode'] == $expected;
} catch (\Exception $e) {
2012-03-23 17:54:38 +00:00
return false;
}
}
}