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:
Ralf Becker 2008-02-18 06:43:49 +00:00
parent 9aa040bec2
commit 9c649da978
6 changed files with 1023 additions and 445 deletions

View File

@ -1,49 +1,54 @@
<?php
/**
* FileManger - 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 filemanger
* @author 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 $
*/
/**
* check if the given user has access
*
* Create a session or if the user has no account return authenticate header and 401 Unauthorized
*
* @param array &$account
* @return int session-id
*/
function check_access(&$account)
{
$account = array(
'login' => $_SERVER['PHP_AUTH_USER'],
'passwd' => $_SERVER['PHP_AUTH_PW'],
'passwd_type' => 'text',
);
if (!($sessionid = $GLOBALS['egw']->session->create($account)))
{
header('WWW-Authenticate: Basic realm="eGroupWare WebDAV"');
header("HTTP/1.1 401 Unauthorized");
header("X-WebDAV-Status: 401 Unauthorized", true);
exit;
}
return $sessionid;
}
$GLOBALS['egw_info']['flags'] = array(
'disable_Template_class' => True,
'noheader' => True,
'currentapp' => 'filemanager',
'autocreate_session_callback' => 'check_access',
);
// if you move this file somewhere else, you need to adapt the path to the header!
include('../header.inc.php');
ExecMethod('phpgwapi.vfs_webdav_server.ServeRequest');
<?php
/**
* FileManger - 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 filemanger
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2006 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* check if the given user has access
*
* Create a session or if the user has no account return authenticate header and 401 Unauthorized
*
* @param array &$account
* @return int session-id
*/
function check_access(&$account)
{
$account = array(
'login' => $_SERVER['PHP_AUTH_USER'],
'passwd' => $_SERVER['PHP_AUTH_PW'],
'passwd_type' => 'text',
);
if (!($sessionid = $GLOBALS['egw']->session->create($account)))
{
header('WWW-Authenticate: Basic realm="eGroupWare WebDAV"');
header("HTTP/1.1 401 Unauthorized");
header("X-WebDAV-Status: 401 Unauthorized", true);
exit;
}
return $sessionid;
}
$GLOBALS['egw_info']['flags'] = array(
'disable_Template_class' => True,
'noheader' => True,
'currentapp' => 'filemanager',
'autocreate_session_callback' => 'check_access',
);
// if you move this file somewhere else, you need to adapt the path to the header!
include('../header.inc.php');
// 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');

View File

@ -59,6 +59,9 @@
*/
class egw_vfs extends vfs_stream_wrapper
{
const EXECUTABLE = 4;
const READABLE = 2;
const WRITABLE = 1;
/**
* 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!");
}
return self::url_stat($path);
return self::url_stat($path,0);
}
@ -185,6 +188,96 @@ class egw_vfs extends vfs_stream_wrapper
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
*/

View File

@ -11,9 +11,6 @@
* @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
*
@ -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
*
*/
const USE_FILESYSTEM_DIRECT = true;
/**
* Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-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
*
@ -145,7 +148,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
'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 (!($options & STREAM_URL_STAT_QUIET))
{
@ -164,7 +167,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
'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 (!($options & STREAM_URL_STAT_QUIET))
{
@ -457,7 +460,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
'must_exist' => true,
)) || ($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!");
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,
)))
{
self::remove_password($url_from);
self::remove_password($url_to);
self::_remove_password($url_from);
self::_remove_password($url_to);
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!");
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))
{
$is_dir = $type_to === self::DIR_MIME_TYPE ? 'a' : 'no';
self::remove_password($url_from);
self::remove_password($url_to);
self::_remove_password($url_from);
self::_remove_password($url_to);
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
}
@ -576,7 +579,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
'must_exist' => false,
)))
{
self::remove_password($url);
self::_remove_password($url);
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
if (!($options & STREAM_URL_STAT_QUIET))
{
@ -616,7 +619,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
'must_exist' => true,
)) || ($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 (!($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!
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 (!($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'] &&
$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!");
$this->opened_dir = null;
return false;
@ -769,7 +772,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
//print_r($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!");
return false;
}
@ -848,12 +851,14 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper
($info['mime_type'] == self::DIR_MIME_TYPE ? 040070 : 0100060),
'size' => $info['size'],
'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']),
'ctime' => strtotime($info['created']),
'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;
}
@ -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);
}
/**
* Replace the password of an url with '...' for error messages
*
* @param string &$url
*/
static private function remove_password(&$url)
static private function _remove_password(&$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');

View 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;
}
}

View File

@ -22,9 +22,13 @@
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';
/**
* 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
*
@ -40,7 +44,8 @@ class vfs_stream_wrapper implements iface_stream_wrapper
* @var array
*/
protected static $fstab = array(
'/' => 'oldvfs://$user:$pass@$host/',
'/' => 'sqlfs://$user:$pass@$host/',
// '/' => 'oldvfs://$user:$pass@$host/',
// '/files' => 'oldvfs://$user:$pass@$host/home/Default',
// '/images' => 'http://localhost/egroupware/phpgwapi/templates/idots/images',
// '/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];
}
$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'] = '/';
@ -319,7 +334,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
{
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 rmdir($path,$options);
return rmdir($url);
}
/**
@ -366,6 +381,60 @@ class vfs_stream_wrapper implements iface_stream_wrapper
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().
*
@ -431,6 +500,7 @@ class vfs_stream_wrapper implements iface_stream_wrapper
{
return false;
}
error_log(__METHOD__."('$path',$flags) calling stat($url)");
return stat($url);
}
@ -498,8 +568,9 @@ class vfs_stream_wrapper implements iface_stream_wrapper
self::$wrappers[] = 'webdav';
break;
case 'oldvfs':
require_once(EGW_API_INC.'/class.oldvfs_stream_wrapper.inc.php');
self::$wrappers[] = 'oldvfs';
case 'sqlfs':
require_once(EGW_API_INC.'/class.'.$scheme.'_stream_wrapper.inc.php');
self::$wrappers[] = $scheme;
break;
case '':
return true; // default file, always loaded

View File

@ -1,37 +1,40 @@
<?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
* @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>
* @author Hartmut Holzgraefe <hartmut@php.net> original HTTP/WebDAV/Server/Filesystem class, of which some code is used
* @version $Id$
*/
require_once('HTTP/WebDAV/Server.php');
require_once(EGW_API_INC.'/class.vfs_home.inc.php');
require_once('HTTP/WebDAV/Server/Filesystem.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';
/**
* 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
*
@ -41,354 +44,151 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
*/
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'");
parent::HTTP_WebDAV_Server();
// special treatment for litmus compliance test
// reply on its identifier header
// 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
*
* @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;
/**
* DELETE method handler
*
* @param array general parameter passing array
* @return bool true on success
*/
function DELETE($options)
{
$path = $this->base . "/" .$options["path"];
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')
if (!file_exists($path))
{
return "404 Not found";
}
if (is_dir($path))
{
/*$query = "DELETE FROM {$this->db_prefix}properties
WHERE path LIKE '".$this->_slashify($options["path"])."%'";
mysql_query($query); */
// recursive delete the directory
if ($dir = egw_vfs::dir_opendir($options["path"]))
{
$info['props'][] = $this->mkprop('resourcetype', 'collection');
$info['props'][] = $this->mkprop('getcontenttype', 'httpd/unix-directory');
while(($file = readdir($dir)))
{
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
{
$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;
}
}
else
{
unlink($path);
}
/*$query = "DELETE FROM {$this->db_prefix}properties
WHERE path = '$options[path]'";
mysql_query($query);*/
return "204 No Content";
}
/**
* Get properties for a single file/resource
*
* @param string resource path
* @return array resource properties
*/
function fileinfo($path)
{
error_log(__METHOD__."($path)");
// map URI path to filesystem path
$fspath = $this->base . $path;
// create result array
$info = array();
// TODO remove slash append code when base clase is able to do it itself
$info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path;
$info["props"] = array();
// no special beautified displayname here ...
$info["props"][] = $this->mkprop("displayname", strtoupper($path));
// creation and modification time
$info["props"][] = $this->mkprop("creationdate", filectime($fspath));
$info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
// type and size (caller already made sure that path exists)
if (is_dir($fspath)) {
// directory (WebDAV collection)
$info["props"][] = $this->mkprop("resourcetype", "collection");
$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
*
* @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
* Used eg. by get
*
* @param array general parameter passing array
* @return bool true on success
* @todo replace all calls to _mimetype with egw_vfs::mime_content_type()
* @param string $path
* @return string
*/
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)
function _mimetype($path)
{
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';
return egw_vfs::mime_content_type($path);
}
/**
@ -404,6 +204,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
*/
function PROPPATCH(&$options)
{
$path = $GLOBALS['egw']->translation->convert($options['path'],'utf-8');
foreach ($options["props"] as $key => $prop) {
$attributes = array();
switch($prop['ns'])
@ -414,10 +216,11 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
{
case 'srt_modifiedtime':
case 'getlastmodified':
$attributes['modified'] = strtotime($prop['val']);
egw_vfs::touch($path,strtotime($prop['val']));
break;
case 'srt_creationtime':
$attributes['created'] = strtotime($prop['val']);
// not supported via the streamwrapper interface atm.
//$attributes['created'] = strtotime($prop['val']);
break;
}
break;
@ -427,7 +230,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
{
// allow netdrive to change the modification time
case 'getlastmodified':
$attributes['modified'] = strtotime($prop['val']);
egw_vfs::touch($path,strtotime($prop['val']));
break;
// not sure why, the filesystem example of the WebDAV class does it ...
default:
@ -438,26 +241,147 @@ class vfs_webdav_server extends HTTP_WebDAV_Server
}
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)));
error_log(__METHOD__.": path=$options[path], props=".implode(', ',$props));
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 ...
}
/**
* 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
*
* @param string $type