mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-02-16 18:31:26 +01:00
WebDAV can use now the new stream wrapper interface, thought its switched off by default in filemanager/webdav.php (see the comments at the end of the file)
This commit is contained in:
parent
9aa040bec2
commit
9c649da978
@ -1,49 +1,54 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* FileManger - WebDAV access
|
* FileManger - WebDAV access
|
||||||
*
|
*
|
||||||
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
||||||
*
|
*
|
||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
* @package filemanger
|
* @package filemanger
|
||||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
* @copyright (c) 2006 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @copyright (c) 2006 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
* @version $Id: class.boetemplate.inc.php 21437 2006-04-24 20:42:42Z ralfbecker $
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if the given user has access
|
* check if the given user has access
|
||||||
*
|
*
|
||||||
* Create a session or if the user has no account return authenticate header and 401 Unauthorized
|
* Create a session or if the user has no account return authenticate header and 401 Unauthorized
|
||||||
*
|
*
|
||||||
* @param array &$account
|
* @param array &$account
|
||||||
* @return int session-id
|
* @return int session-id
|
||||||
*/
|
*/
|
||||||
function check_access(&$account)
|
function check_access(&$account)
|
||||||
{
|
{
|
||||||
$account = array(
|
$account = array(
|
||||||
'login' => $_SERVER['PHP_AUTH_USER'],
|
'login' => $_SERVER['PHP_AUTH_USER'],
|
||||||
'passwd' => $_SERVER['PHP_AUTH_PW'],
|
'passwd' => $_SERVER['PHP_AUTH_PW'],
|
||||||
'passwd_type' => 'text',
|
'passwd_type' => 'text',
|
||||||
);
|
);
|
||||||
if (!($sessionid = $GLOBALS['egw']->session->create($account)))
|
if (!($sessionid = $GLOBALS['egw']->session->create($account)))
|
||||||
{
|
{
|
||||||
header('WWW-Authenticate: Basic realm="eGroupWare WebDAV"');
|
header('WWW-Authenticate: Basic realm="eGroupWare WebDAV"');
|
||||||
header("HTTP/1.1 401 Unauthorized");
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
header("X-WebDAV-Status: 401 Unauthorized", true);
|
header("X-WebDAV-Status: 401 Unauthorized", true);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
return $sessionid;
|
return $sessionid;
|
||||||
}
|
}
|
||||||
|
|
||||||
$GLOBALS['egw_info']['flags'] = array(
|
$GLOBALS['egw_info']['flags'] = array(
|
||||||
'disable_Template_class' => True,
|
'disable_Template_class' => True,
|
||||||
'noheader' => True,
|
'noheader' => True,
|
||||||
'currentapp' => 'filemanager',
|
'currentapp' => 'filemanager',
|
||||||
'autocreate_session_callback' => 'check_access',
|
'autocreate_session_callback' => 'check_access',
|
||||||
);
|
);
|
||||||
// if you move this file somewhere else, you need to adapt the path to the header!
|
// if you move this file somewhere else, you need to adapt the path to the header!
|
||||||
include('../header.inc.php');
|
include('../header.inc.php');
|
||||||
|
|
||||||
ExecMethod('phpgwapi.vfs_webdav_server.ServeRequest');
|
// only enable one of the following WebDAV server:
|
||||||
|
// 1. this uses the old webdav class, using the old vfs classes direct (1.4 and current default)
|
||||||
|
ExecMethod('phpgwapi.oldvfs_webdav_server.ServeRequest');
|
||||||
|
|
||||||
|
// 2. this uses the new streamwrapper VFS interface
|
||||||
|
//ExecMethod('phpgwapi.vfs_webdav_server.ServeRequest');
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
*/
|
*/
|
||||||
class egw_vfs extends vfs_stream_wrapper
|
class egw_vfs extends vfs_stream_wrapper
|
||||||
{
|
{
|
||||||
|
const EXECUTABLE = 4;
|
||||||
|
const READABLE = 2;
|
||||||
|
const WRITABLE = 1;
|
||||||
/**
|
/**
|
||||||
* fopen working on just the eGW VFS
|
* fopen working on just the eGW VFS
|
||||||
*
|
*
|
||||||
@ -129,7 +132,7 @@ class egw_vfs extends vfs_stream_wrapper
|
|||||||
{
|
{
|
||||||
throw new egw_exception_assertion_failed("File '$path' is not an absolute path!");
|
throw new egw_exception_assertion_failed("File '$path' is not an absolute path!");
|
||||||
}
|
}
|
||||||
return self::url_stat($path);
|
return self::url_stat($path,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -185,6 +188,96 @@ class egw_vfs extends vfs_stream_wrapper
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stream_wrapper interface checks is_{readable|writable|executable} against the webservers uid,
|
||||||
|
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param int $check=4 mode to check: 4 = read, 2 = write, 1 = executable
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
static function is_readable($path,$check = 4)
|
||||||
|
{
|
||||||
|
if (!($stat = self::stat($path)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return self::check_access($stat,$check);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stream_wrapper interface checks is_{readable|writable|executable} against the webservers uid,
|
||||||
|
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||||
|
*
|
||||||
|
* @param array $stat
|
||||||
|
* @param int $check mode to check: 4 = read, 2 = write, 1 = executable
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
static function check_access($stat,$check)
|
||||||
|
{
|
||||||
|
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)");
|
||||||
|
|
||||||
|
if (!$stat)
|
||||||
|
{
|
||||||
|
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) no stat array!");
|
||||||
|
return false; // file not found
|
||||||
|
}
|
||||||
|
// check if other rights grant access
|
||||||
|
if ($stat['mode'] & $check)
|
||||||
|
{
|
||||||
|
error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via other rights!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// check if there's owner access and we are the owner
|
||||||
|
if (($stat['mode'] & ($check << 6)) && $stat['uid'] && $stat['uid'] == $GLOBALS['egw_info']['user']['account_id'])
|
||||||
|
{
|
||||||
|
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via owner rights!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// check if there's a group access and we have the right membership
|
||||||
|
if (($stat['mode'] & ($check << 3)) && $stat['gid'])
|
||||||
|
{
|
||||||
|
static $memberships;
|
||||||
|
if (is_null($memberships))
|
||||||
|
{
|
||||||
|
$memberships = $GLOBALS['egw']->accounts->memberships($GLOBALS['egw_info']['user']['account_id'],true);
|
||||||
|
}
|
||||||
|
if (in_array(-abs($stat['gid']),$memberships))
|
||||||
|
{
|
||||||
|
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via group rights!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// here could be a check for extended acls ...
|
||||||
|
|
||||||
|
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) no access!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stream_wrapper interface checks is_{readable|writable|executable} against the webservers uid,
|
||||||
|
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
static function is_writable($path)
|
||||||
|
{
|
||||||
|
return self::is_readable($path,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stream_wrapper interface checks is_{readable|writable|executable} against the webservers uid,
|
||||||
|
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
static function is_executable($path)
|
||||||
|
{
|
||||||
|
return self::is_readable($path,1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor to prevent instanciating this class, only it's static methods should be used
|
* Private constructor to prevent instanciating this class, only it's static methods should be used
|
||||||
*/
|
*/
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once(EGW_API_INC.'/class.vfs_home.inc.php');
|
|
||||||
require_once(EGW_API_INC.'/class.iface_stream_wrapper.inc.php');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eGroupWare API: VFS - old (until eGW 1.4 inclusive) VFS stream wrapper
|
* eGroupWare API: VFS - old (until eGW 1.4 inclusive) VFS stream wrapper
|
||||||
*
|
*
|
||||||
@ -27,14 +24,20 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* If this class should do the operations direct in the filesystem, instead of going through the vfs
|
* If this class should do the operations direct in the filesystem, instead of going through the vfs
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
const USE_FILESYSTEM_DIRECT = true;
|
const USE_FILESYSTEM_DIRECT = true;
|
||||||
/**
|
/**
|
||||||
* Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
* Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
const DIR_MIME_TYPE = 'Directory';
|
const DIR_MIME_TYPE = 'Directory';
|
||||||
|
/**
|
||||||
|
* Scheme / protocoll used for this stream-wrapper
|
||||||
|
*/
|
||||||
|
const SCHEME = 'oldvfs';
|
||||||
|
/**
|
||||||
|
* Does url_stat returns a mime type, or has it to be determined otherwise (string with attribute name)
|
||||||
|
*/
|
||||||
|
const STAT_RETURN_MIME_TYPE = 'mime';
|
||||||
/**
|
/**
|
||||||
* How much should be logged to the apache error-log
|
* How much should be logged to the apache error-log
|
||||||
*
|
*
|
||||||
@ -145,7 +148,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'operation' => EGW_ACL_ADD,
|
'operation' => EGW_ACL_ADD,
|
||||||
)))
|
)))
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file does not exist or can not be created!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file does not exist or can not be created!");
|
||||||
if (!($options & STREAM_URL_STAT_QUIET))
|
if (!($options & STREAM_URL_STAT_QUIET))
|
||||||
{
|
{
|
||||||
@ -164,7 +167,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'operation' => EGW_ACL_EDIT,
|
'operation' => EGW_ACL_EDIT,
|
||||||
)))
|
)))
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file can not be edited!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file can not be edited!");
|
||||||
if (!($options & STREAM_URL_STAT_QUIET))
|
if (!($options & STREAM_URL_STAT_QUIET))
|
||||||
{
|
{
|
||||||
@ -457,7 +460,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'must_exist' => true,
|
'must_exist' => true,
|
||||||
)) || ($type = self::$old_vfs->file_type($data)) === self::DIR_MIME_TYPE)
|
)) || ($type = self::$old_vfs->file_type($data)) === self::DIR_MIME_TYPE)
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url) (type=$type) permission denied!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url) (type=$type) permission denied!");
|
||||||
return false; // no permission or file does not exist
|
return false; // no permission or file does not exist
|
||||||
}
|
}
|
||||||
@ -509,8 +512,8 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'operation' => EGW_ACL_ADD,
|
'operation' => EGW_ACL_ADD,
|
||||||
)))
|
)))
|
||||||
{
|
{
|
||||||
self::remove_password($url_from);
|
self::_remove_password($url_from);
|
||||||
self::remove_password($url_to);
|
self::_remove_password($url_to);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!");
|
||||||
return false; // no permission or file does not exist
|
return false; // no permission or file does not exist
|
||||||
}
|
}
|
||||||
@ -520,8 +523,8 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
($type_to === self::DIR_MIME_TYPE) !== (self::$old_vfs->file_type($data_from) === self::DIR_MIME_TYPE))
|
($type_to === self::DIR_MIME_TYPE) !== (self::$old_vfs->file_type($data_from) === self::DIR_MIME_TYPE))
|
||||||
{
|
{
|
||||||
$is_dir = $type_to === self::DIR_MIME_TYPE ? 'a' : 'no';
|
$is_dir = $type_to === self::DIR_MIME_TYPE ? 'a' : 'no';
|
||||||
self::remove_password($url_from);
|
self::_remove_password($url_from);
|
||||||
self::remove_password($url_to);
|
self::_remove_password($url_to);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $path_to is $is_dir directory!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $path_to is $is_dir directory!");
|
||||||
return false; // no permission or file does not exist
|
return false; // no permission or file does not exist
|
||||||
}
|
}
|
||||||
@ -576,7 +579,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'must_exist' => false,
|
'must_exist' => false,
|
||||||
)))
|
)))
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
|
||||||
if (!($options & STREAM_URL_STAT_QUIET))
|
if (!($options & STREAM_URL_STAT_QUIET))
|
||||||
{
|
{
|
||||||
@ -616,7 +619,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
'must_exist' => true,
|
'must_exist' => true,
|
||||||
)) || ($type = self::$old_vfs->file_type($data)) !== self::DIR_MIME_TYPE)
|
)) || ($type = self::$old_vfs->file_type($data)) !== self::DIR_MIME_TYPE)
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) (type=$type) permission denied!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) (type=$type) permission denied!");
|
||||||
if (!($options & STREAM_URL_STAT_QUIET))
|
if (!($options & STREAM_URL_STAT_QUIET))
|
||||||
{
|
{
|
||||||
@ -628,7 +631,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
// our vfs deletes recursive, while the stream-wrapper interface does not!
|
// our vfs deletes recursive, while the stream-wrapper interface does not!
|
||||||
if (($files = self::$old_vfs->ls($data)))
|
if (($files = self::$old_vfs->ls($data)))
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) dir is not empty!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) dir is not empty!");
|
||||||
if (!($options & STREAM_URL_STAT_QUIET))
|
if (!($options & STREAM_URL_STAT_QUIET))
|
||||||
{
|
{
|
||||||
@ -699,7 +702,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
count($files) == 1 && $path == $files[0]['directory'].'/'.$files[0]['name'] &&
|
count($files) == 1 && $path == $files[0]['directory'].'/'.$files[0]['name'] &&
|
||||||
$files[0]['mime_type'] != self::DIR_MIME_TYPE)
|
$files[0]['mime_type'] != self::DIR_MIME_TYPE)
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$options) $url is not directory!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$options) $url is not directory!");
|
||||||
$this->opened_dir = null;
|
$this->opened_dir = null;
|
||||||
return false;
|
return false;
|
||||||
@ -769,7 +772,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
//print_r($info);
|
//print_r($info);
|
||||||
if (!$info)
|
if (!$info)
|
||||||
{
|
{
|
||||||
self::remove_password($url);
|
self::_remove_password($url);
|
||||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$flags) file or directory not found!");
|
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$flags) file or directory not found!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -848,12 +851,14 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
($info['mime_type'] == self::DIR_MIME_TYPE ? 040070 : 0100060),
|
($info['mime_type'] == self::DIR_MIME_TYPE ? 040070 : 0100060),
|
||||||
'size' => $info['size'],
|
'size' => $info['size'],
|
||||||
'uid' => $info['owner_id'] > 0 ? $info['owner_id'] : 0,
|
'uid' => $info['owner_id'] > 0 ? $info['owner_id'] : 0,
|
||||||
'gid' => $info['owner_id'] < 0 ? $info['owner_id'] : 0,
|
'gid' => $info['owner_id'] < 0 ? -$info['owner_id'] : 0,
|
||||||
'mtime' => strtotime($info['modified'] ? $info['modified'] : $info['created']),
|
'mtime' => strtotime($info['modified'] ? $info['modified'] : $info['created']),
|
||||||
'ctime' => strtotime($info['created']),
|
'ctime' => strtotime($info['created']),
|
||||||
'nlink' => $info['mime_type'] == self::DIR_MIME_TYPE ? 2 : 1,
|
'nlink' => $info['mime_type'] == self::DIR_MIME_TYPE ? 2 : 1,
|
||||||
|
// eGW addition to return the mime type
|
||||||
|
'mime' => $info['mime_type'],
|
||||||
);
|
);
|
||||||
//print_r($stat);
|
//error_log(__METHOD__."($info[name]) = ".print_r($stat,true));
|
||||||
return $stat;
|
return $stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,13 +878,13 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
|
|
||||||
return $func_overload & 2 ? mb_substr($str,$start,$length,'ascii') : substr($str,$start,$length);
|
return $func_overload & 2 ? mb_substr($str,$start,$length,'ascii') : substr($str,$start,$length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the password of an url with '...' for error messages
|
* Replace the password of an url with '...' for error messages
|
||||||
*
|
*
|
||||||
* @param string &$url
|
* @param string &$url
|
||||||
*/
|
*/
|
||||||
static private function remove_password(&$url)
|
static private function _remove_password(&$url)
|
||||||
{
|
{
|
||||||
$parts = parse_url($url);
|
$parts = parse_url($url);
|
||||||
|
|
||||||
@ -891,4 +896,4 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_register_wrapper('oldvfs','oldvfs_stream_wrapper');
|
stream_register_wrapper(oldvfs_stream_wrapper::SCHEME ,'oldvfs_stream_wrapper');
|
||||||
|
480
phpgwapi/inc/class.oldvfs_webdav_server.inc.php
Normal file
480
phpgwapi/inc/class.oldvfs_webdav_server.inc.php
Normal file
@ -0,0 +1,480 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* eGroupWare API: VFS - WebDAV access
|
||||||
|
*
|
||||||
|
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
||||||
|
*
|
||||||
|
* @link http://www.egroupware.org
|
||||||
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
|
* @package api
|
||||||
|
* @subpackage vfs
|
||||||
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
|
* @copyright (c) 2006 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('HTTP/WebDAV/Server.php');
|
||||||
|
require_once(EGW_API_INC.'/class.vfs_home.inc.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FileManger - WebDAV access
|
||||||
|
*
|
||||||
|
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
||||||
|
*/
|
||||||
|
class oldvfs_webdav_server extends HTTP_WebDAV_Server
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* instance of the vfs class
|
||||||
|
*
|
||||||
|
* @var vfs_home
|
||||||
|
*/
|
||||||
|
var $vfs;
|
||||||
|
|
||||||
|
var $dav_powered_by = 'eGroupWare WebDAV server';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array
|
||||||
|
*
|
||||||
|
* The debug messages are send to the apache error_log
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
var $debug = 0;
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
if ($this->debug === 2) foreach($_SERVER as $name => $val) error_log("vfs_webdav_server: \$_SERVER[$name]='$val'");
|
||||||
|
|
||||||
|
parent::HTTP_WebDAV_Server();
|
||||||
|
|
||||||
|
$this->vfs =& new vfs_home;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PROPFIND method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @param array return array for file properties
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function PROPFIND(&$options, &$files)
|
||||||
|
{
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
// at first only list the given path itself
|
||||||
|
'checksubdirs' => False,
|
||||||
|
'nofiles' => True
|
||||||
|
);
|
||||||
|
if (!($vfs_files = $this->vfs->ls($vfs_data))) // path not found
|
||||||
|
{
|
||||||
|
// check if the users home-dir is just not yet created (should be done by the vfs-class!)
|
||||||
|
// ToDo: group-dirs
|
||||||
|
if ($vfs_data['string'] == '/home/'.$GLOBALS['egw_info']['user']['account_lid'])
|
||||||
|
{
|
||||||
|
$this->vfs->override_acl = true; // user has no right to create dir in /home
|
||||||
|
$created = $this->vfs->mkdir(array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
));
|
||||||
|
$this->vfs->override_acl = false;
|
||||||
|
|
||||||
|
if (!$created)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) could not create home dir");
|
||||||
|
}
|
||||||
|
$vfs_files = $this->vfs->ls($vfs_data);
|
||||||
|
}
|
||||||
|
if (!$vfs_files)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) return false (path not found)");
|
||||||
|
return false; // path not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if depth > 0 and path is a directory => show it's contents
|
||||||
|
if (!empty($options['depth']) && $vfs_files[0]['mime_type'] == 'Directory')
|
||||||
|
{
|
||||||
|
$vfs_data['checksubdirs'] = (int) $options['depth'] != 1;
|
||||||
|
$vfs_data['nofiles'] = false;
|
||||||
|
|
||||||
|
if ($vfs_files[0]['directory'] == '/') // sub-dirs of the root?
|
||||||
|
{
|
||||||
|
$vfs_files = array(); // dont return the directory, it shows up double in konq
|
||||||
|
}
|
||||||
|
else // return the dir itself with a trailing slash, otherwise empty dirs are reported as non-existent
|
||||||
|
{
|
||||||
|
$vfs_files[0]['name'] .= '/';
|
||||||
|
}
|
||||||
|
$vfs_files = array_merge($vfs_files,$this->vfs->ls($vfs_data));
|
||||||
|
}
|
||||||
|
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) ".count($vfs_files).' files');
|
||||||
|
|
||||||
|
$files['files'] = array();
|
||||||
|
$egw_charset = $GLOBALS['egw']->translation->charset();
|
||||||
|
foreach($vfs_files as $fileinfo)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('dir="'.$fileinfo['directory'].'", name="'.$fileinfo['name'].'": '.$fileinfo['mime_type']);
|
||||||
|
foreach(array('modified','created') as $date)
|
||||||
|
{
|
||||||
|
// our vfs has no modified set, if never modified, use created
|
||||||
|
list($y,$m,$d,$h,$i,$s) = split("[- :]",$fileinfo[$date] ? $fileinfo[$date] : $fileinfo['created']);
|
||||||
|
$fileinfo[$date] = mktime((int)$h,(int)$i,(int)$s,(int)$m,(int)$d,(int)$y);
|
||||||
|
}
|
||||||
|
$info = array(
|
||||||
|
'path' => $GLOBALS['egw']->translation->convert($fileinfo['directory'].'/'.$fileinfo['name'],$egw_charset,'utf-8'),
|
||||||
|
'props' => array(
|
||||||
|
$this->mkprop('displayname',$GLOBALS['egw']->translation->convert($fileinfo['name'],$egw_charset,'utf-8')),
|
||||||
|
$this->mkprop('creationdate',$fileinfo['created']),
|
||||||
|
$this->mkprop('getlastmodified',$fileinfo['modified']),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if ($fileinfo['mime_type'] == 'Directory')
|
||||||
|
{
|
||||||
|
$info['props'][] = $this->mkprop('resourcetype', 'collection');
|
||||||
|
$info['props'][] = $this->mkprop('getcontenttype', 'httpd/unix-directory');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$info['props'][] = $this->mkprop('resourcetype', '');
|
||||||
|
$info['props'][] = $this->mkprop('getcontenttype', $fileinfo['mime_type']);
|
||||||
|
$info['props'][] = $this->mkprop('getcontentlength', $fileinfo['size']);
|
||||||
|
}
|
||||||
|
$files['files'][] = $info;
|
||||||
|
}
|
||||||
|
if ($this->debug == 2) foreach($files['files'] as $info) error_log(print_r($info,true));
|
||||||
|
// ok, all done
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET method handler
|
||||||
|
*
|
||||||
|
* @param array parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function GET(&$options)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('vfs_webdav_server::GET('.print_r($options,true).')');
|
||||||
|
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
'checksubdirs' => False,
|
||||||
|
'nofiles' => True
|
||||||
|
);
|
||||||
|
// sanity check
|
||||||
|
if (!($vfs_file = $this->vfs->ls($vfs_data)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$options['mimetype'] = $vfs_file[0]['mime_type'];
|
||||||
|
$options['size'] = $vfs_file[0]['size'];
|
||||||
|
|
||||||
|
if (($options['data'] = $this->vfs->read($vfs_data)) === false)
|
||||||
|
{
|
||||||
|
return '403 Forbidden'; // not sure if this is the right code for access denied
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT method handler
|
||||||
|
*
|
||||||
|
* @param array parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function PUT(&$options)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('vfs_webdav_server::PUT('.print_r($options,true).')');
|
||||||
|
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => dirname($GLOBALS['egw']->translation->convert($options['path'],'utf-8')),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
'checksubdirs' => False,
|
||||||
|
'nofiles' => True
|
||||||
|
);
|
||||||
|
if (!($vfs_file = $this->vfs->ls($vfs_data)) || $vfs_file[0]['mime_type'] != 'Directory')
|
||||||
|
{
|
||||||
|
return '409 Conflict';
|
||||||
|
}
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
);
|
||||||
|
$options['new'] = !$this->vfs->file_exists($vfs_data);
|
||||||
|
|
||||||
|
$vfs_data['content'] = '';
|
||||||
|
while(!feof($options['stream']))
|
||||||
|
{
|
||||||
|
$vfs_data['content'] .= fread($options['stream'],8192);
|
||||||
|
}
|
||||||
|
return $this->vfs->write($vfs_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MKCOL method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function MKCOL($options)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('vfs_webdav_server::MKCOL('.print_r($options,true).')');
|
||||||
|
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => dirname($GLOBALS['egw']->translation->convert($options['path'],'utf-8')),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
'checksubdirs' => False,
|
||||||
|
'nofiles' => True
|
||||||
|
);
|
||||||
|
if (!($vfs_file = $this->vfs->ls($vfs_data)))
|
||||||
|
{
|
||||||
|
return '409 Conflict';
|
||||||
|
}
|
||||||
|
if ($this->debug) error_log(print_r($vfs_file,true));
|
||||||
|
|
||||||
|
if ($vfs_file[0]['mime_type'] != 'Directory')
|
||||||
|
{
|
||||||
|
return '403 Forbidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
);
|
||||||
|
if ($this->vfs->file_exists($vfs_data) )
|
||||||
|
{
|
||||||
|
return '405 Method not allowed';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_SERVER['CONTENT_LENGTH'])) // no body parsing yet
|
||||||
|
{
|
||||||
|
return '415 Unsupported media type';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->vfs->mkdir($vfs_data))
|
||||||
|
{
|
||||||
|
return '403 Forbidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '201 Created';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function DELETE($options)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('vfs_webdav_server::DELETE('.print_r($options,true).')');
|
||||||
|
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
);
|
||||||
|
if (!$this->vfs->file_exists($vfs_data))
|
||||||
|
{
|
||||||
|
return '404 Not found';
|
||||||
|
}
|
||||||
|
if (!$this->vfs->rm($vfs_data))
|
||||||
|
{
|
||||||
|
return '403 Forbidden';
|
||||||
|
}
|
||||||
|
return '204 No Content';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MOVE method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function MOVE($options)
|
||||||
|
{
|
||||||
|
return $this->COPY($options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COPY method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function COPY($options, $del=false)
|
||||||
|
{
|
||||||
|
if ($this->debug) error_log('vfs_webdav_server::'.($del ? 'MOVE' : 'COPY').'('.print_r($options,true).')');
|
||||||
|
|
||||||
|
// TODO Property updates still broken (Litmus should detect this?)
|
||||||
|
|
||||||
|
if (!empty($_SERVER['CONTENT_LENGTH'])) // no body parsing yet
|
||||||
|
{
|
||||||
|
return '415 Unsupported media type';
|
||||||
|
}
|
||||||
|
|
||||||
|
// no copying to different WebDAV Servers yet
|
||||||
|
if (isset($options['dest_url']))
|
||||||
|
{
|
||||||
|
return '502 bad gateway';
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
);
|
||||||
|
if (!$this->vfs->file_exists($source))
|
||||||
|
{
|
||||||
|
return '404 Not found';
|
||||||
|
}
|
||||||
|
|
||||||
|
$dest = array(
|
||||||
|
'string' => $options['dest'],
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
);
|
||||||
|
$new = !$this->vfs->file_exists($dest);
|
||||||
|
$existing_col = false;
|
||||||
|
|
||||||
|
if (!$new)
|
||||||
|
{
|
||||||
|
if ($del && $this->vfs->file_type($dest) == 'Directory')
|
||||||
|
{
|
||||||
|
if (!$options['overwrite'])
|
||||||
|
{
|
||||||
|
return '412 precondition failed';
|
||||||
|
}
|
||||||
|
$dest['string'] .= basename($GLOBALS['egw']->translation->convert($options['path'],'utf-8'));
|
||||||
|
if ($this->vfs->file_exists($dest))
|
||||||
|
{
|
||||||
|
$options['dest'] .= basename($GLOBALS['egw']->translation->convert($options['path'],'utf-8'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$new = true;
|
||||||
|
$existing_col = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$new)
|
||||||
|
{
|
||||||
|
if ($options['overwrite'])
|
||||||
|
{
|
||||||
|
$stat = $this->DELETE(array('path' => $options['dest']));
|
||||||
|
if (($stat{0} != '2') && (substr($stat, 0, 3) != '404'))
|
||||||
|
{
|
||||||
|
return $stat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return '412 precondition failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->vfs->file_type($source) == 'Directory' && ($options['depth'] != 'infinity'))
|
||||||
|
{
|
||||||
|
// RFC 2518 Section 9.2, last paragraph
|
||||||
|
return '400 Bad request';
|
||||||
|
}
|
||||||
|
|
||||||
|
$op = $del ? 'mv' : 'cp';
|
||||||
|
$vfs_data = array(
|
||||||
|
'from' => $source['string'],
|
||||||
|
'to' => $dest['string'],
|
||||||
|
'relatives' => array(RELATIVE_ROOT,RELATIVE_ROOT)
|
||||||
|
);
|
||||||
|
if (!$this->vfs->$op($vfs_data))
|
||||||
|
{
|
||||||
|
return '500 Internal server error';
|
||||||
|
}
|
||||||
|
return ($new && !$existing_col) ? '201 Created' : '204 No Content';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PROPPATCH method handler
|
||||||
|
*
|
||||||
|
* The current version only allows Webdrive to set creation and modificaton dates.
|
||||||
|
* They are not stored as (arbitrary) WebDAV properties with their own namespace and name,
|
||||||
|
* but in the regular vfs attributes.
|
||||||
|
*
|
||||||
|
* @todo Store a properties in the DB and retrieve them in PROPFIND again.
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function PROPPATCH(&$options)
|
||||||
|
{
|
||||||
|
foreach ($options["props"] as $key => $prop) {
|
||||||
|
$attributes = array();
|
||||||
|
switch($prop['ns'])
|
||||||
|
{
|
||||||
|
// allow Webdrive to set creation and modification time
|
||||||
|
case 'http://www.southrivertech.com/':
|
||||||
|
switch($prop['name'])
|
||||||
|
{
|
||||||
|
case 'srt_modifiedtime':
|
||||||
|
case 'getlastmodified':
|
||||||
|
$attributes['modified'] = strtotime($prop['val']);
|
||||||
|
break;
|
||||||
|
case 'srt_creationtime':
|
||||||
|
$attributes['created'] = strtotime($prop['val']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DAV:':
|
||||||
|
switch($prop['name'])
|
||||||
|
{
|
||||||
|
// allow netdrive to change the modification time
|
||||||
|
case 'getlastmodified':
|
||||||
|
$attributes['modified'] = strtotime($prop['val']);
|
||||||
|
break;
|
||||||
|
// not sure why, the filesystem example of the WebDAV class does it ...
|
||||||
|
default:
|
||||||
|
$options["props"][$key]['status'] = "403 Forbidden";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($this->debug) $props[] = '('.$prop["ns"].')'.$prop['name'].'='.$prop['val'];
|
||||||
|
}
|
||||||
|
if ($attributes)
|
||||||
|
{
|
||||||
|
$vfs_data = array(
|
||||||
|
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
||||||
|
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
||||||
|
'attributes'=> $attributes,
|
||||||
|
);
|
||||||
|
$this->vfs->set_attributes($vfs_data);
|
||||||
|
}
|
||||||
|
if ($this->debug)
|
||||||
|
{
|
||||||
|
error_log(__CLASS__.'::'.__METHOD__.": path=$options[path], props=".implode(', ',$props));
|
||||||
|
if ($attributes) error_log(__CLASS__.'::'.__METHOD__.": path=$options[path], set attributes=".str_replace("\n",' ',print_r($attributes,true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ""; // this is as the filesystem example handler does it, no true or false ...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* auth check in the session creation in dav.php, to avoid being redirected to login.php
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param string $login account_lid or account_lid@domain
|
||||||
|
* @param string $password this is checked in the session creation
|
||||||
|
* @return boolean true if authorized or false otherwise
|
||||||
|
*/
|
||||||
|
function checkAuth($type,$login,$password)
|
||||||
|
{
|
||||||
|
list($account_lid,$domain) = explode('@',$login);
|
||||||
|
|
||||||
|
$auth = ($login === $GLOBALS['egw_info']['user']['account_lid'] ||
|
||||||
|
($account_lid === $GLOBALS['egw_info']['user']['account_lid'] && $domain === $GLOBALS['egw']->session->account_domain)) &&
|
||||||
|
$GLOBALS['egw_info']['user']['apps']['filemanager'];
|
||||||
|
|
||||||
|
if ($this->debug) error_log("vfs_webdav_server::checkAuth('$type','$login','\$password'): account_lid='$account_lid', domain='$domain' ==> ".(int)$auth);
|
||||||
|
|
||||||
|
return $auth;
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,13 @@
|
|||||||
class vfs_stream_wrapper implements iface_stream_wrapper
|
class vfs_stream_wrapper implements iface_stream_wrapper
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Scheme / protocoll used for this stream-wrapper
|
* Scheme / protocol used for this stream-wrapper
|
||||||
*/
|
*/
|
||||||
const SCHEME = 'vfs';
|
const SCHEME = 'vfs';
|
||||||
|
/**
|
||||||
|
* Mime type of directories, the old vfs used 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
||||||
|
*/
|
||||||
|
const DIR_MIME_TYPE = 'httpd/unix-directory';
|
||||||
/**
|
/**
|
||||||
* optional context param when opening the stream, null if no context passed
|
* optional context param when opening the stream, null if no context passed
|
||||||
*
|
*
|
||||||
@ -40,7 +44,8 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $fstab = array(
|
protected static $fstab = array(
|
||||||
'/' => 'oldvfs://$user:$pass@$host/',
|
'/' => 'sqlfs://$user:$pass@$host/',
|
||||||
|
// '/' => 'oldvfs://$user:$pass@$host/',
|
||||||
// '/files' => 'oldvfs://$user:$pass@$host/home/Default',
|
// '/files' => 'oldvfs://$user:$pass@$host/home/Default',
|
||||||
// '/images' => 'http://localhost/egroupware/phpgwapi/templates/idots/images',
|
// '/images' => 'http://localhost/egroupware/phpgwapi/templates/idots/images',
|
||||||
// '/home/ralf/linux' => '/home/ralf', // we probably need to forbid direct filesystem access for security reasons!
|
// '/home/ralf/linux' => '/home/ralf', // we probably need to forbid direct filesystem access for security reasons!
|
||||||
@ -88,7 +93,17 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
{
|
{
|
||||||
return $cache[$path];
|
return $cache[$path];
|
||||||
}
|
}
|
||||||
$parts = parse_url($path);
|
// setting default user, passwd and domain, if it's not contained int the url
|
||||||
|
static $defaults;
|
||||||
|
if (is_null($defaults))
|
||||||
|
{
|
||||||
|
$defaults = array(
|
||||||
|
'user' => $GLOBALS['egw_info']['user']['account_lid'],
|
||||||
|
'pass' => $GLOBALS['egw_info']['user']['passwd'],
|
||||||
|
'host' => $GLOBALS['egw_info']['user']['domain'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$parts = array_merge(parse_url($path),$defaults);
|
||||||
|
|
||||||
if (empty($parts['path'])) $parts['path'] = '/';
|
if (empty($parts['path'])) $parts['path'] = '/';
|
||||||
|
|
||||||
@ -319,7 +334,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return mkdir($path,$mode,$options);
|
return mkdir($url,$mode,$options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -338,7 +353,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return rmdir($path,$options);
|
return rmdir($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -366,6 +381,60 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
return touch($url,$time);
|
return touch($url,$time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
|
||||||
|
*
|
||||||
|
* The methods use the following ways to get the mime type (in that order)
|
||||||
|
* - directories (is_dir()) --> self::DIR_MIME_TYPE
|
||||||
|
* - stream implemented by class defining the STAT_RETURN_MIME_TYPE constant --> use mime-type returned by url_stat
|
||||||
|
* - for regular filesystem use mime_content_type function if available
|
||||||
|
* - use eGW's mime-magic class
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @return string mime-type (self::DIR_MIME_TYPE for directories)
|
||||||
|
*/
|
||||||
|
static function mime_content_type($path)
|
||||||
|
{
|
||||||
|
if (!($url = self::resolve_url($path)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (is_dir($url))
|
||||||
|
{
|
||||||
|
$mime = self::DIR_MIME_TYPE;
|
||||||
|
}
|
||||||
|
if (!$mime && ($scheme = parse_url($url,PHP_URL_SCHEME)))
|
||||||
|
{
|
||||||
|
// check it it's an eGW stream wrapper returning mime-type via url_stat
|
||||||
|
if (class_exists($class = $scheme.'_stream_wrapper') && ($mime_attr = constant($class.'::STAT_RETURN_MIME_TYPE')))
|
||||||
|
{
|
||||||
|
$stat = call_user_func(array($scheme.'_stream_wrapper','url_stat'),parse_url($url,PHP_URL_PATH),0);
|
||||||
|
if ($stat[$mime_attr])
|
||||||
|
{
|
||||||
|
$mime = $stat[$mime_attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we operate on the regular filesystem and the mime_content_type function is available --> use it
|
||||||
|
if (!$mime && !$scheme && function_exists('mime_content_type'))
|
||||||
|
{
|
||||||
|
$mime = mime_content_type($path);
|
||||||
|
}
|
||||||
|
// using eGW's own mime magic
|
||||||
|
// ToDo: rework mime_magic as all methods cound be static!
|
||||||
|
if (!$mime)
|
||||||
|
{
|
||||||
|
static $mime_magic;
|
||||||
|
if (is_null($mime_magic))
|
||||||
|
{
|
||||||
|
$mime_magic = mime_magic();
|
||||||
|
}
|
||||||
|
$mime = $mime_magic->filename2mime(parse_url($url,PHP_URL_PATH));
|
||||||
|
}
|
||||||
|
//error_log(__METHOD__."($path) mime=$mime");
|
||||||
|
return $mime;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called immediately when your stream object is created for examining directory contents with opendir().
|
* This method is called immediately when your stream object is created for examining directory contents with opendir().
|
||||||
*
|
*
|
||||||
@ -431,6 +500,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
error_log(__METHOD__."('$path',$flags) calling stat($url)");
|
||||||
return stat($url);
|
return stat($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,8 +568,9 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
|||||||
self::$wrappers[] = 'webdav';
|
self::$wrappers[] = 'webdav';
|
||||||
break;
|
break;
|
||||||
case 'oldvfs':
|
case 'oldvfs':
|
||||||
require_once(EGW_API_INC.'/class.oldvfs_stream_wrapper.inc.php');
|
case 'sqlfs':
|
||||||
self::$wrappers[] = 'oldvfs';
|
require_once(EGW_API_INC.'/class.'.$scheme.'_stream_wrapper.inc.php');
|
||||||
|
self::$wrappers[] = $scheme;
|
||||||
break;
|
break;
|
||||||
case '':
|
case '':
|
||||||
return true; // default file, always loaded
|
return true; // default file, always loaded
|
||||||
|
@ -1,37 +1,40 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* eGroupWare API: VFS - WebDAV access
|
* eGroupWare API: VFS - WebDAV access using the new stream wrapper VFS interface
|
||||||
*
|
*
|
||||||
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
* Using the PEAR HTTP/WebDAV/Server/Filesystem class (which need to be installed!)
|
||||||
*
|
*
|
||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
* @package api
|
* @package api
|
||||||
* @subpackage vfs
|
* @subpackage vfs
|
||||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
* @copyright (c) 2006 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @author Hartmut Holzgraefe <hartmut@php.net> original HTTP/WebDAV/Server/Filesystem class, of which some code is used
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once('HTTP/WebDAV/Server.php');
|
require_once('HTTP/WebDAV/Server/Filesystem.php');
|
||||||
require_once(EGW_API_INC.'/class.vfs_home.inc.php');
|
require_once(EGW_API_INC.'/class.egw_vfs.inc.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FileManger - WebDAV access
|
* FileManger - WebDAV access using the new stream wrapper VFS interface
|
||||||
*
|
*
|
||||||
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
|
* Using the PEAR HTTP/WebDAV/Server/Filesystem class (which need to be installed!)
|
||||||
|
*
|
||||||
|
* @todo table to store locks and properties
|
||||||
|
* @todo filesystem class uses PEAR's System::find which we dont require nor know if it works on custom streamwrapper
|
||||||
*/
|
*/
|
||||||
class vfs_webdav_server extends HTTP_WebDAV_Server
|
class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* instance of the vfs class
|
|
||||||
*
|
|
||||||
* @var vfs_home
|
|
||||||
*/
|
|
||||||
var $vfs;
|
|
||||||
|
|
||||||
var $dav_powered_by = 'eGroupWare WebDAV server';
|
var $dav_powered_by = 'eGroupWare WebDAV server';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base directory is the URL of our VFS root
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
var $base = 'vfs://default';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array
|
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array
|
||||||
*
|
*
|
||||||
@ -41,354 +44,151 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
|
|||||||
*/
|
*/
|
||||||
var $debug = 0;
|
var $debug = 0;
|
||||||
|
|
||||||
function vfs_webdav_server()
|
/**
|
||||||
|
* Serve a webdav request
|
||||||
|
*
|
||||||
|
* Reimplemented to not check our vfs base path with realpath and connect to mysql DB
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
function ServeRequest($base = false)
|
||||||
{
|
{
|
||||||
if ($this->debug === 2) foreach($_SERVER as $name => $val) error_log("vfs_webdav_server: \$_SERVER[$name]='$val'");
|
// special treatment for litmus compliance test
|
||||||
|
// reply on its identifier header
|
||||||
parent::HTTP_WebDAV_Server();
|
// not needed for the test itself but eases debugging
|
||||||
|
foreach (apache_request_headers() as $key => $value)
|
||||||
|
{
|
||||||
|
if (stristr($key, "litmus"))
|
||||||
|
{
|
||||||
|
error_log("Litmus test $value");
|
||||||
|
header("X-Litmus-reply: ".$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->vfs =& new vfs_home;
|
// let the base class do all the work
|
||||||
|
HTTP_WebDAV_Server::ServeRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PROPFIND method handler
|
* DELETE method handler
|
||||||
*
|
*
|
||||||
* @param array general parameter passing array
|
* @param array general parameter passing array
|
||||||
* @param array return array for file properties
|
* @return bool true on success
|
||||||
* @return bool true on success
|
*/
|
||||||
*/
|
function DELETE($options)
|
||||||
function PROPFIND(&$options, &$files)
|
{
|
||||||
{
|
$path = $this->base . "/" .$options["path"];
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
// at first only list the given path itself
|
|
||||||
'checksubdirs' => False,
|
|
||||||
'nofiles' => True
|
|
||||||
);
|
|
||||||
if (!($vfs_files = $this->vfs->ls($vfs_data))) // path not found
|
|
||||||
{
|
|
||||||
// check if the users home-dir is just not yet created (should be done by the vfs-class!)
|
|
||||||
// ToDo: group-dirs
|
|
||||||
if ($vfs_data['string'] == '/home/'.$GLOBALS['egw_info']['user']['account_lid'])
|
|
||||||
{
|
|
||||||
$this->vfs->override_acl = true; // user has no right to create dir in /home
|
|
||||||
$created = $this->vfs->mkdir(array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
));
|
|
||||||
$this->vfs->override_acl = false;
|
|
||||||
|
|
||||||
if (!$created)
|
|
||||||
{
|
|
||||||
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) could not create home dir");
|
|
||||||
}
|
|
||||||
$vfs_files = $this->vfs->ls($vfs_data);
|
|
||||||
}
|
|
||||||
if (!$vfs_files)
|
|
||||||
{
|
|
||||||
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) return false (path not found)");
|
|
||||||
return false; // path not found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if depth > 0 and path is a directory => show it's contents
|
|
||||||
if (!empty($options['depth']) && $vfs_files[0]['mime_type'] == 'Directory')
|
|
||||||
{
|
|
||||||
$vfs_data['checksubdirs'] = (int) $options['depth'] != 1;
|
|
||||||
$vfs_data['nofiles'] = false;
|
|
||||||
|
|
||||||
if ($vfs_files[0]['directory'] == '/') // sub-dirs of the root?
|
if (!file_exists($path))
|
||||||
{
|
{
|
||||||
$vfs_files = array(); // dont return the directory, it shows up double in konq
|
return "404 Not found";
|
||||||
}
|
}
|
||||||
else // return the dir itself with a trailing slash, otherwise empty dirs are reported as non-existent
|
|
||||||
{
|
if (is_dir($path))
|
||||||
$vfs_files[0]['name'] .= '/';
|
{
|
||||||
}
|
/*$query = "DELETE FROM {$this->db_prefix}properties
|
||||||
$vfs_files = array_merge($vfs_files,$this->vfs->ls($vfs_data));
|
WHERE path LIKE '".$this->_slashify($options["path"])."%'";
|
||||||
}
|
mysql_query($query); */
|
||||||
if ($this->debug) error_log("vfs_webdav_server::PROPFIND(path='$options[path]',depth=$options[depth]) ".count($vfs_files).' files');
|
// recursive delete the directory
|
||||||
|
if ($dir = egw_vfs::dir_opendir($options["path"]))
|
||||||
$files['files'] = array();
|
|
||||||
$egw_charset = $GLOBALS['egw']->translation->charset();
|
|
||||||
foreach($vfs_files as $fileinfo)
|
|
||||||
{
|
|
||||||
if ($this->debug) error_log('dir="'.$fileinfo['directory'].'", name="'.$fileinfo['name'].'": '.$fileinfo['mime_type']);
|
|
||||||
foreach(array('modified','created') as $date)
|
|
||||||
{
|
|
||||||
// our vfs has no modified set, if never modified, use created
|
|
||||||
list($y,$m,$d,$h,$i,$s) = split("[- :]",$fileinfo[$date] ? $fileinfo[$date] : $fileinfo['created']);
|
|
||||||
$fileinfo[$date] = mktime((int)$h,(int)$i,(int)$s,(int)$m,(int)$d,(int)$y);
|
|
||||||
}
|
|
||||||
$info = array(
|
|
||||||
'path' => $GLOBALS['egw']->translation->convert($fileinfo['directory'].'/'.$fileinfo['name'],$egw_charset,'utf-8'),
|
|
||||||
'props' => array(
|
|
||||||
$this->mkprop('displayname',$GLOBALS['egw']->translation->convert($fileinfo['name'],$egw_charset,'utf-8')),
|
|
||||||
$this->mkprop('creationdate',$fileinfo['created']),
|
|
||||||
$this->mkprop('getlastmodified',$fileinfo['modified']),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if ($fileinfo['mime_type'] == 'Directory')
|
|
||||||
{
|
{
|
||||||
$info['props'][] = $this->mkprop('resourcetype', 'collection');
|
while(($file = readdir($dir)))
|
||||||
$info['props'][] = $this->mkprop('getcontenttype', 'httpd/unix-directory');
|
{
|
||||||
|
if ($file == '.' || $file == '..') continue;
|
||||||
|
|
||||||
|
if (is_dir($path.'/'.$file))
|
||||||
|
{
|
||||||
|
// recursivly call ourself with the dir
|
||||||
|
$opts = $options;
|
||||||
|
$opts['path'] .= '/'.$file;
|
||||||
|
$this->DELETE($opts);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unlink($path.'/'.$file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dir);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
$info['props'][] = $this->mkprop('resourcetype', '');
|
{
|
||||||
$info['props'][] = $this->mkprop('getcontenttype', $fileinfo['mime_type']);
|
unlink($path);
|
||||||
$info['props'][] = $this->mkprop('getcontentlength', $fileinfo['size']);
|
}
|
||||||
}
|
/*$query = "DELETE FROM {$this->db_prefix}properties
|
||||||
$files['files'][] = $info;
|
WHERE path = '$options[path]'";
|
||||||
}
|
mysql_query($query);*/
|
||||||
if ($this->debug == 2) foreach($files['files'] as $info) error_log(print_r($info,true));
|
|
||||||
// ok, all done
|
return "204 No Content";
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Get properties for a single file/resource
|
||||||
* GET method handler
|
*
|
||||||
*
|
* @param string resource path
|
||||||
* @param array parameter passing array
|
* @return array resource properties
|
||||||
* @return bool true on success
|
*/
|
||||||
*/
|
function fileinfo($path)
|
||||||
function GET(&$options)
|
{
|
||||||
{
|
error_log(__METHOD__."($path)");
|
||||||
if ($this->debug) error_log('vfs_webdav_server::GET('.print_r($options,true).')');
|
// map URI path to filesystem path
|
||||||
|
$fspath = $this->base . $path;
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
// create result array
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
$info = array();
|
||||||
'checksubdirs' => False,
|
// TODO remove slash append code when base clase is able to do it itself
|
||||||
'nofiles' => True
|
$info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path;
|
||||||
);
|
$info["props"] = array();
|
||||||
// sanity check
|
|
||||||
if (!($vfs_file = $this->vfs->ls($vfs_data)))
|
// no special beautified displayname here ...
|
||||||
{
|
$info["props"][] = $this->mkprop("displayname", strtoupper($path));
|
||||||
return false;
|
|
||||||
}
|
// creation and modification time
|
||||||
$options['mimetype'] = $vfs_file[0]['mime_type'];
|
$info["props"][] = $this->mkprop("creationdate", filectime($fspath));
|
||||||
$options['size'] = $vfs_file[0]['size'];
|
$info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
|
||||||
|
|
||||||
if (($options['data'] = $this->vfs->read($vfs_data)) === false)
|
// type and size (caller already made sure that path exists)
|
||||||
{
|
if (is_dir($fspath)) {
|
||||||
return '403 Forbidden'; // not sure if this is the right code for access denied
|
// directory (WebDAV collection)
|
||||||
}
|
$info["props"][] = $this->mkprop("resourcetype", "collection");
|
||||||
return true;
|
$info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
|
||||||
}
|
} else {
|
||||||
|
// plain file (WebDAV resource)
|
||||||
|
$info["props"][] = $this->mkprop("resourcetype", "");
|
||||||
|
if (egw_vfs::is_readable($path)) {
|
||||||
|
$info["props"][] = $this->mkprop("getcontenttype", egw_vfs::mime_content_type($path));
|
||||||
|
} else {
|
||||||
|
error_log(__METHOD__."($path) $fspath is not readable!");
|
||||||
|
$info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
|
||||||
|
}
|
||||||
|
$info["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// get additional properties from database
|
||||||
|
$query = "SELECT ns, name, value
|
||||||
|
FROM {$this->db_prefix}properties
|
||||||
|
WHERE path = '$path'";
|
||||||
|
$res = mysql_query($query);
|
||||||
|
while ($row = mysql_fetch_assoc($res)) {
|
||||||
|
$info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
|
||||||
|
}
|
||||||
|
mysql_free_result($res);
|
||||||
|
*/
|
||||||
|
//error_log(__METHOD__."($path) info=".print_r($info,true));
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PUT method handler
|
* Used eg. by get
|
||||||
*
|
|
||||||
* @param array parameter passing array
|
|
||||||
* @return bool true on success
|
|
||||||
*/
|
|
||||||
function PUT(&$options)
|
|
||||||
{
|
|
||||||
if ($this->debug) error_log('vfs_webdav_server::PUT('.print_r($options,true).')');
|
|
||||||
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => dirname($GLOBALS['egw']->translation->convert($options['path'],'utf-8')),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
'checksubdirs' => False,
|
|
||||||
'nofiles' => True
|
|
||||||
);
|
|
||||||
if (!($vfs_file = $this->vfs->ls($vfs_data)) || $vfs_file[0]['mime_type'] != 'Directory')
|
|
||||||
{
|
|
||||||
return '409 Conflict';
|
|
||||||
}
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
);
|
|
||||||
$options['new'] = !$this->vfs->file_exists($vfs_data);
|
|
||||||
|
|
||||||
$vfs_data['content'] = '';
|
|
||||||
while(!feof($options['stream']))
|
|
||||||
{
|
|
||||||
$vfs_data['content'] .= fread($options['stream'],8192);
|
|
||||||
}
|
|
||||||
return $this->vfs->write($vfs_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MKCOL method handler
|
|
||||||
*
|
*
|
||||||
* @param array general parameter passing array
|
* @todo replace all calls to _mimetype with egw_vfs::mime_content_type()
|
||||||
* @return bool true on success
|
* @param string $path
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
function MKCOL($options)
|
function _mimetype($path)
|
||||||
{
|
|
||||||
if ($this->debug) error_log('vfs_webdav_server::MKCOL('.print_r($options,true).')');
|
|
||||||
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => dirname($GLOBALS['egw']->translation->convert($options['path'],'utf-8')),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
'checksubdirs' => False,
|
|
||||||
'nofiles' => True
|
|
||||||
);
|
|
||||||
if (!($vfs_file = $this->vfs->ls($vfs_data)))
|
|
||||||
{
|
|
||||||
return '409 Conflict';
|
|
||||||
}
|
|
||||||
if ($this->debug) error_log(print_r($vfs_file,true));
|
|
||||||
|
|
||||||
if ($vfs_file[0]['mime_type'] != 'Directory')
|
|
||||||
{
|
|
||||||
return '403 Forbidden';
|
|
||||||
}
|
|
||||||
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
);
|
|
||||||
if ($this->vfs->file_exists($vfs_data) )
|
|
||||||
{
|
|
||||||
return '405 Method not allowed';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_SERVER['CONTENT_LENGTH'])) // no body parsing yet
|
|
||||||
{
|
|
||||||
return '415 Unsupported media type';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->vfs->mkdir($vfs_data))
|
|
||||||
{
|
|
||||||
return '403 Forbidden';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '201 Created';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DELETE method handler
|
|
||||||
*
|
|
||||||
* @param array general parameter passing array
|
|
||||||
* @return bool true on success
|
|
||||||
*/
|
|
||||||
function DELETE($options)
|
|
||||||
{
|
{
|
||||||
if ($this->debug) error_log('vfs_webdav_server::DELETE('.print_r($options,true).')');
|
return egw_vfs::mime_content_type($path);
|
||||||
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
);
|
|
||||||
if (!$this->vfs->file_exists($vfs_data))
|
|
||||||
{
|
|
||||||
return '404 Not found';
|
|
||||||
}
|
|
||||||
if (!$this->vfs->rm($vfs_data))
|
|
||||||
{
|
|
||||||
return '403 Forbidden';
|
|
||||||
}
|
|
||||||
return '204 No Content';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MOVE method handler
|
|
||||||
*
|
|
||||||
* @param array general parameter passing array
|
|
||||||
* @return bool true on success
|
|
||||||
*/
|
|
||||||
function MOVE($options)
|
|
||||||
{
|
|
||||||
return $this->COPY($options, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* COPY method handler
|
|
||||||
*
|
|
||||||
* @param array general parameter passing array
|
|
||||||
* @return bool true on success
|
|
||||||
*/
|
|
||||||
function COPY($options, $del=false)
|
|
||||||
{
|
|
||||||
if ($this->debug) error_log('vfs_webdav_server::'.($del ? 'MOVE' : 'COPY').'('.print_r($options,true).')');
|
|
||||||
|
|
||||||
// TODO Property updates still broken (Litmus should detect this?)
|
|
||||||
|
|
||||||
if (!empty($_SERVER['CONTENT_LENGTH'])) // no body parsing yet
|
|
||||||
{
|
|
||||||
return '415 Unsupported media type';
|
|
||||||
}
|
|
||||||
|
|
||||||
// no copying to different WebDAV Servers yet
|
|
||||||
if (isset($options['dest_url']))
|
|
||||||
{
|
|
||||||
return '502 bad gateway';
|
|
||||||
}
|
|
||||||
|
|
||||||
$source = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
);
|
|
||||||
if (!$this->vfs->file_exists($source))
|
|
||||||
{
|
|
||||||
return '404 Not found';
|
|
||||||
}
|
|
||||||
|
|
||||||
$dest = array(
|
|
||||||
'string' => $options['dest'],
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
);
|
|
||||||
$new = !$this->vfs->file_exists($dest);
|
|
||||||
$existing_col = false;
|
|
||||||
|
|
||||||
if (!$new)
|
|
||||||
{
|
|
||||||
if ($del && $this->vfs->file_type($dest) == 'Directory')
|
|
||||||
{
|
|
||||||
if (!$options['overwrite'])
|
|
||||||
{
|
|
||||||
return '412 precondition failed';
|
|
||||||
}
|
|
||||||
$dest['string'] .= basename($GLOBALS['egw']->translation->convert($options['path'],'utf-8'));
|
|
||||||
if ($this->vfs->file_exists($dest))
|
|
||||||
{
|
|
||||||
$options['dest'] .= basename($GLOBALS['egw']->translation->convert($options['path'],'utf-8'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$new = true;
|
|
||||||
$existing_col = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$new)
|
|
||||||
{
|
|
||||||
if ($options['overwrite'])
|
|
||||||
{
|
|
||||||
$stat = $this->DELETE(array('path' => $options['dest']));
|
|
||||||
if (($stat{0} != '2') && (substr($stat, 0, 3) != '404'))
|
|
||||||
{
|
|
||||||
return $stat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return '412 precondition failed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->vfs->file_type($source) == 'Directory' && ($options['depth'] != 'infinity'))
|
|
||||||
{
|
|
||||||
// RFC 2518 Section 9.2, last paragraph
|
|
||||||
return '400 Bad request';
|
|
||||||
}
|
|
||||||
|
|
||||||
$op = $del ? 'mv' : 'cp';
|
|
||||||
$vfs_data = array(
|
|
||||||
'from' => $source['string'],
|
|
||||||
'to' => $dest['string'],
|
|
||||||
'relatives' => array(RELATIVE_ROOT,RELATIVE_ROOT)
|
|
||||||
);
|
|
||||||
if (!$this->vfs->$op($vfs_data))
|
|
||||||
{
|
|
||||||
return '500 Internal server error';
|
|
||||||
}
|
|
||||||
return ($new && !$existing_col) ? '201 Created' : '204 No Content';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -404,6 +204,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
|
|||||||
*/
|
*/
|
||||||
function PROPPATCH(&$options)
|
function PROPPATCH(&$options)
|
||||||
{
|
{
|
||||||
|
$path = $GLOBALS['egw']->translation->convert($options['path'],'utf-8');
|
||||||
|
|
||||||
foreach ($options["props"] as $key => $prop) {
|
foreach ($options["props"] as $key => $prop) {
|
||||||
$attributes = array();
|
$attributes = array();
|
||||||
switch($prop['ns'])
|
switch($prop['ns'])
|
||||||
@ -414,10 +216,11 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
|
|||||||
{
|
{
|
||||||
case 'srt_modifiedtime':
|
case 'srt_modifiedtime':
|
||||||
case 'getlastmodified':
|
case 'getlastmodified':
|
||||||
$attributes['modified'] = strtotime($prop['val']);
|
egw_vfs::touch($path,strtotime($prop['val']));
|
||||||
break;
|
break;
|
||||||
case 'srt_creationtime':
|
case 'srt_creationtime':
|
||||||
$attributes['created'] = strtotime($prop['val']);
|
// not supported via the streamwrapper interface atm.
|
||||||
|
//$attributes['created'] = strtotime($prop['val']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -427,7 +230,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
|
|||||||
{
|
{
|
||||||
// allow netdrive to change the modification time
|
// allow netdrive to change the modification time
|
||||||
case 'getlastmodified':
|
case 'getlastmodified':
|
||||||
$attributes['modified'] = strtotime($prop['val']);
|
egw_vfs::touch($path,strtotime($prop['val']));
|
||||||
break;
|
break;
|
||||||
// not sure why, the filesystem example of the WebDAV class does it ...
|
// not sure why, the filesystem example of the WebDAV class does it ...
|
||||||
default:
|
default:
|
||||||
@ -438,26 +241,147 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
|
|||||||
}
|
}
|
||||||
if ($this->debug) $props[] = '('.$prop["ns"].')'.$prop['name'].'='.$prop['val'];
|
if ($this->debug) $props[] = '('.$prop["ns"].')'.$prop['name'].'='.$prop['val'];
|
||||||
}
|
}
|
||||||
if ($attributes)
|
|
||||||
{
|
|
||||||
$vfs_data = array(
|
|
||||||
'string' => $GLOBALS['egw']->translation->convert($options['path'],'utf-8'),
|
|
||||||
'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root
|
|
||||||
'attributes'=> $attributes,
|
|
||||||
);
|
|
||||||
$this->vfs->set_attributes($vfs_data);
|
|
||||||
}
|
|
||||||
if ($this->debug)
|
if ($this->debug)
|
||||||
{
|
{
|
||||||
error_log(__CLASS__.'::'.__METHOD__.": path=$options[path], props=".implode(', ',$props));
|
error_log(__METHOD__.": path=$options[path], props=".implode(', ',$props));
|
||||||
if ($attributes) error_log(__CLASS__.'::'.__METHOD__.": path=$options[path], set attributes=".str_replace("\n",' ',print_r($attributes,true)));
|
if ($attributes) error_log(__METHOD__.": path=$options[path], set attributes=".str_replace("\n",' ',print_r($attributes,true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return ""; // this is as the filesystem example handler does it, no true or false ...
|
return ""; // this is as the filesystem example handler does it, no true or false ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LOCK method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function LOCK(&$options)
|
||||||
|
{
|
||||||
|
// behaving like LOCK is not implemented
|
||||||
|
return "412 Precondition failed";
|
||||||
|
/*
|
||||||
|
// get absolute fs path to requested resource
|
||||||
|
$fspath = $this->base . $options["path"];
|
||||||
|
|
||||||
|
// TODO recursive locks on directories not supported yet
|
||||||
|
if (is_dir($fspath) && !empty($options["depth"])) {
|
||||||
|
return "409 Conflict";
|
||||||
|
}
|
||||||
|
|
||||||
|
$options["timeout"] = time()+300; // 5min. hardcoded
|
||||||
|
|
||||||
|
if (isset($options["update"])) { // Lock Update
|
||||||
|
$where = "WHERE path = '$options[path]' AND token = '$options[update]'";
|
||||||
|
|
||||||
|
$query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where";
|
||||||
|
$res = mysql_query($query);
|
||||||
|
$row = mysql_fetch_assoc($res);
|
||||||
|
mysql_free_result($res);
|
||||||
|
|
||||||
|
if (is_array($row)) {
|
||||||
|
$query = "UPDATE {$this->db_prefix}locks
|
||||||
|
SET expires = '$options[timeout]'
|
||||||
|
, modified = ".time()."
|
||||||
|
$where";
|
||||||
|
mysql_query($query);
|
||||||
|
|
||||||
|
$options['owner'] = $row['owner'];
|
||||||
|
$options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
|
||||||
|
$options['type'] = $row["exclusivelock"] ? "write" : "read";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = "INSERT INTO {$this->db_prefix}locks
|
||||||
|
SET token = '$options[locktoken]'
|
||||||
|
, path = '$options[path]'
|
||||||
|
, created = ".time()."
|
||||||
|
, modified = ".time()."
|
||||||
|
, owner = '$options[owner]'
|
||||||
|
, expires = '$options[timeout]'
|
||||||
|
, exclusivelock = " .($options['scope'] === "exclusive" ? "1" : "0")
|
||||||
|
;
|
||||||
|
mysql_query($query);
|
||||||
|
|
||||||
|
return mysql_affected_rows() ? "200 OK" : "409 Conflict";*/
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* UNLOCK method handler
|
||||||
|
*
|
||||||
|
* @param array general parameter passing array
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function UNLOCK(&$options)
|
||||||
|
{
|
||||||
|
// behaving like LOCK is not implemented
|
||||||
|
return "405 Method not allowed";
|
||||||
|
/*
|
||||||
|
$query = "DELETE FROM {$this->db_prefix}locks
|
||||||
|
WHERE path = '$options[path]'
|
||||||
|
AND token = '$options[token]'";
|
||||||
|
mysql_query($query);
|
||||||
|
|
||||||
|
return mysql_affected_rows() ? "204 No Content" : "409 Conflict";*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checkLock() helper
|
||||||
|
*
|
||||||
|
* @param string resource path to check for locks
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
function checkLock($path)
|
||||||
|
{
|
||||||
|
// behave like checkLock is not implemented
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
$result = false;
|
||||||
|
|
||||||
|
$query = "SELECT owner, token, created, modified, expires, exclusivelock
|
||||||
|
FROM {$this->db_prefix}locks
|
||||||
|
WHERE path = '$path'
|
||||||
|
";
|
||||||
|
$res = mysql_query($query);
|
||||||
|
|
||||||
|
if ($res) {
|
||||||
|
$row = mysql_fetch_array($res);
|
||||||
|
mysql_free_result($res);
|
||||||
|
|
||||||
|
if ($row) {
|
||||||
|
$result = array( "type" => "write",
|
||||||
|
"scope" => $row["exclusivelock"] ? "exclusive" : "shared",
|
||||||
|
"depth" => 0,
|
||||||
|
"owner" => $row['owner'],
|
||||||
|
"token" => $row['token'],
|
||||||
|
"created" => $row['created'],
|
||||||
|
"modified" => $row['modified'],
|
||||||
|
"expires" => $row['expires']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove not (yet) implemented LOCK methods, so we can use the mostly unchanged HTTP_WebDAV_Server_Filesystem class
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function _allow()
|
||||||
|
{
|
||||||
|
$allow = parent::_allow();
|
||||||
|
unset($allow['LOCK']);
|
||||||
|
unset($allow['UNLOCK']);
|
||||||
|
return $allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* auth check in the session creation in dav.php, to avoid being redirected to login.php
|
* auth check in the session creation in dav.php, to avoid being redirected to login.php
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
|
Loading…
Reference in New Issue
Block a user