mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-22 16:03:47 +01:00
* WebDAV/Filemanager: fixed not working home-directores
This commit is contained in:
parent
a7b41998cf
commit
8bca2eb07f
@ -7,7 +7,7 @@
|
||||
* @package api
|
||||
* @subpackage vfs
|
||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2008-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2008-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
@ -34,11 +34,11 @@
|
||||
*
|
||||
* The two following methods can be used to persitently mount further filesystems (without editing the code):
|
||||
*
|
||||
* - boolean/array egw_vfs::mount($url,$path) to mount $ur on $path or to return the fstab when called without argument
|
||||
* - boolean|array egw_vfs::mount($url,$path) to mount $ur on $path or to return the fstab when called without argument
|
||||
* - boolean egw_vfs::umount($path) to unmount a path or url
|
||||
*
|
||||
* The stream wrapper interface allows to access hugh files in junks to not be limited by the
|
||||
* memory_limit setting of php. To do you should path a resource to the opened file and not the content:
|
||||
* memory_limit setting of php. To do you should pass the opened file as resource and not the content:
|
||||
*
|
||||
* $file = egw_vfs::fopen('/home/user/somefile','r');
|
||||
* $content = fread($file,1024);
|
||||
@ -180,20 +180,42 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
$ret = false;
|
||||
|
||||
if (($from_fp = self::fopen($from,'r')) &&
|
||||
($to_fp = self::fopen($to,'w')))
|
||||
$old_props = self::file_exists($to) ? self::propfind($to,null) : array();
|
||||
// copy properties (eg. file comment), if there are any and evtl. existing old properties
|
||||
$props = self::propfind($from,null);
|
||||
|
||||
foreach($old_props as $prop)
|
||||
{
|
||||
$ret = stream_copy_to_stream($from_fp,$to_fp) !== false;
|
||||
if (!self::find_prop($props,$prop))
|
||||
{
|
||||
$prop['val'] = null; // null = delete prop
|
||||
$props[] = $prop;
|
||||
}
|
||||
}
|
||||
if ($from_fp)
|
||||
{
|
||||
fclose($from_fp);
|
||||
}
|
||||
if ($to_fp)
|
||||
{
|
||||
fclose($to_fp);
|
||||
}
|
||||
return $ret;
|
||||
// using self::copy_uploaded() to treat copying incl. properties as atomar operation in respect of notifications
|
||||
return self::copy_uploaded(self::PREFIX.$from,$to,$props,false); // false = no is_uploaded_file check!
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific property in an array of properties (eg. returned by propfind)
|
||||
*
|
||||
* @param array &$props
|
||||
* @param array|string $name property array or name
|
||||
* @param string $ns=self::DEFAULT_PROP_NAMESPACE namespace, only if $prop is no array
|
||||
* @return &array reference to property in $props or null if not found
|
||||
*/
|
||||
static function &find_prop(array &$props,$name,$ns=self::DEFAULT_PROP_NAMESPACE)
|
||||
{
|
||||
if (is_array($name))
|
||||
{
|
||||
$ns = $name['ns'];
|
||||
$name = $name['name'];
|
||||
}
|
||||
foreach($props as &$prop)
|
||||
{
|
||||
if ($prop['name'] == $name && $prop['ns'] == $ns) return $prop;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,10 +298,14 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*
|
||||
* @param string $url=null url of the filesystem to mount, eg. oldvfs://default/
|
||||
* @param string $path=null path to mount the filesystem in the vfs, eg. /
|
||||
* @return array/boolean array with fstab, if called without parameter or true on successful mount
|
||||
* @param boolean $check_url=null check if url is an existing directory, before mounting it
|
||||
* default null only checks if url does not contain a $ as used in $user or $pass
|
||||
* @return array|boolean array with fstab, if called without parameter or true on successful mount
|
||||
*/
|
||||
static function mount($url=null,$path=null)
|
||||
static function mount($url=null,$path=null,$check_url=null)
|
||||
{
|
||||
if (is_null($check_url)) $check_url = strpos($url,'$') === false;
|
||||
|
||||
if (!isset($GLOBALS['egw_info']['server']['vfs_fstab'])) // happens eg. in setup
|
||||
{
|
||||
$api_config = config::read('phpgwapi');
|
||||
@ -306,7 +332,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
}
|
||||
self::load_wrapper(parse_url($url,PHP_URL_SCHEME));
|
||||
|
||||
if (!file_exists($url) || opendir($url) === false)
|
||||
if ($check_url && (!file_exists($url) || opendir($url) === false))
|
||||
{
|
||||
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') url does NOT exist!');
|
||||
return false; // url does not exist
|
||||
@ -317,7 +343,11 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
|
||||
config::save_value('vfs_fstab',self::$fstab,'phpgwapi');
|
||||
$GLOBALS['egw_info']['server']['vfs_fstab'] = self::$fstab;
|
||||
|
||||
// invalidate session cache
|
||||
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited
|
||||
{
|
||||
$GLOBALS['egw']->invalidate_session_cache();
|
||||
}
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') returns true (successful new mount).');
|
||||
return true;
|
||||
}
|
||||
@ -343,15 +373,32 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
|
||||
config::save_value('vfs_fstab',self::$fstab,'phpgwapi');
|
||||
$GLOBALS['egw_info']['server']['vfs_fstab'] = self::$fstab;
|
||||
|
||||
// invalidate session cache
|
||||
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited
|
||||
{
|
||||
$GLOBALS['egw']->invalidate_session_cache();
|
||||
}
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') returns true (successful unmount).');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file is hidden: name starts with a '.' or is Thumbs.db
|
||||
*
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_hidden($path)
|
||||
{
|
||||
$file = self::basename($path);
|
||||
|
||||
return $file[0] == '.' || $file == 'Thumbs.db';
|
||||
}
|
||||
|
||||
/**
|
||||
* find = recursive search over the filesystem
|
||||
*
|
||||
* @param string/array $base base of the search
|
||||
* @param string|array $base base of the search
|
||||
* @param array $options=null the following keys are allowed:
|
||||
* - type => {d|f|F} d=dirs, f=files (incl. symlinks), F=files (incl. symlinks to files), default all
|
||||
* - depth => {true|false(default)} put the contents of a dir before the dir itself
|
||||
@ -371,7 +418,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* - limit => N,[n=0] return N entries from position n on, which defaults to 0
|
||||
* - follow => {true|false(default)} follow symlinks
|
||||
* - hidden => {true|false(default)} include hidden files (name starts with a '.' or is Thumbs.db)
|
||||
* @param string/array/true $exec=null function to call with each found file/dir as first param and stat array as last param or
|
||||
* @param string|array/true $exec=null function to call with each found file/dir as first param and stat array as last param or
|
||||
* true to return file => stat pairs
|
||||
* @param array $exec_params=null further params for exec as array, path is always the first param and stat the last!
|
||||
* @return array of pathes if no $exec, otherwise path => stat pairs
|
||||
@ -447,7 +494,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
if ($file == '.' || $file == '..') continue; // ignore current and parent dir!
|
||||
|
||||
if (($file[0] == '.' || $file == 'Thumbs.db') && !$options['hidden']) continue; // ignore hidden files
|
||||
if (self::is_hidden($file) && !$options['hidden']) continue; // ignore hidden files
|
||||
|
||||
$file = self::concat($path,$file);
|
||||
|
||||
@ -571,7 +618,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
|
||||
if ($options['url'])
|
||||
{
|
||||
$stat = lstat($path);
|
||||
$stat = @lstat($path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -653,7 +700,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
/**
|
||||
* Recursiv remove all given url's, including it's content if they are files
|
||||
*
|
||||
* @param string/array $urls url or array of url's
|
||||
* @param string|array $urls url or array of url's
|
||||
* @param boolean $allow_urls=false allow to use url's, default no only pathes (to stay within the vfs)
|
||||
* @return array
|
||||
*/
|
||||
@ -685,9 +732,9 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
}
|
||||
if (is_dir($url) && !is_link($url))
|
||||
{
|
||||
return rmdir($url);
|
||||
return egw_vfs::rmdir($url,0);
|
||||
}
|
||||
return unlink($url);
|
||||
return egw_vfs::unlink($url);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -695,10 +742,11 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* 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
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = egw_vfs::READABLE,
|
||||
* 2 = egw_vfs::WRITABLE, 1 = egw_vfs::EXECUTABLE
|
||||
* @return boolean
|
||||
*/
|
||||
static function is_readable($path,$check = 4)
|
||||
static function is_readable($path,$check = self::READABLE)
|
||||
{
|
||||
return self::check_access($path,$check);
|
||||
}
|
||||
@ -707,13 +755,52 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* 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/string $path stat array or path
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = read, 2 = write, 1 = executable
|
||||
* @param array $stat=null stat array, to not query it again
|
||||
* @param string $path path
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = egw_vfs::READABLE,
|
||||
* 2 = egw_vfs::WRITABLE, 1 = egw_vfs::EXECUTABLE
|
||||
* @param array|boolean $stat=null stat array or false, to not query it again
|
||||
* @param int $user=null user used for check, if not current user (egw_vfs::$user)
|
||||
* @return boolean
|
||||
*/
|
||||
static function check_access($path,$check,$stat=null)
|
||||
static function check_access($path, $check, $stat=null, $user=null)
|
||||
{
|
||||
if (is_null($stat) && $user && $user != self::$user)
|
||||
{
|
||||
static $path_user_stat = array();
|
||||
|
||||
$backup_user = self::$user;
|
||||
self::$user = $user;
|
||||
|
||||
if (!isset($path_user_stat[$path]) || !isset($path_user_stat[$path][$user]))
|
||||
{
|
||||
self::clearstatcache($path);
|
||||
|
||||
$path_user_stat[$path][$user] = self::url_stat($path, 0);
|
||||
|
||||
self::clearstatcache($path); // we need to clear the stat-cache after the call too, as the next call might be the regular user again!
|
||||
}
|
||||
if (($stat = $path_user_stat[$path][$user]))
|
||||
{
|
||||
// some backend mounts use $user:$pass in their url, for them we have to deny access!
|
||||
if (strpos(self::resolve_url($path, false, false, false), '$user') !== false)
|
||||
{
|
||||
$ret = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = self::check_access($path, $check, $stat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = false; // no access, if we can not stat the file
|
||||
}
|
||||
self::$user = $backup_user;
|
||||
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check,$user) ".array2string($ret));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (self::$is_root)
|
||||
{
|
||||
return true;
|
||||
@ -722,7 +809,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
// throw exception if stat array is used insead of path, can be removed soon
|
||||
if (is_array($path))
|
||||
{
|
||||
throw new egw_exception_wrong_parameter('path has to be string, use check_acces($path,$check,$stat=null)!');
|
||||
throw new egw_exception_wrong_parameter('path has to be string, use check_access($path,$check,$stat=null)!');
|
||||
}
|
||||
// query stat array, if not given
|
||||
if (is_null($stat))
|
||||
@ -736,6 +823,20 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) no stat array!");
|
||||
return false; // file not found
|
||||
}
|
||||
// check if we use an EGroupwre stream wrapper, or a stock php one
|
||||
// if it's not an EGroupware one, we can NOT use uid, gid and mode!
|
||||
if (($scheme = parse_url($stat['url'],PHP_URL_SCHEME)) && !(class_exists(self::scheme2class($scheme))))
|
||||
{
|
||||
switch($check)
|
||||
{
|
||||
case self::READABLE:
|
||||
return is_readable($stat['url']);
|
||||
case self::WRITABLE:
|
||||
return is_writable($stat['url']);
|
||||
case self::EXECUTABLE:
|
||||
return is_executable($stat['url']);
|
||||
}
|
||||
}
|
||||
// check if other rights grant access
|
||||
if (($stat['mode'] & $check) == $check)
|
||||
{
|
||||
@ -751,12 +852,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
// check if there's a group access and we have the right membership
|
||||
if (($stat['mode'] & ($check << 3)) == ($check << 3) && $stat['gid'])
|
||||
{
|
||||
static $memberships;
|
||||
if (is_null($memberships))
|
||||
{
|
||||
$memberships = $GLOBALS['egw']->accounts->memberships(self::$user,true);
|
||||
}
|
||||
if (in_array(-abs($stat['gid']),$memberships))
|
||||
if (($memberships = $GLOBALS['egw']->accounts->memberships(self::$user, true)) && in_array(-abs($stat['gid']), $memberships))
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via group rights!");
|
||||
return true;
|
||||
@ -778,7 +874,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*/
|
||||
static function is_writable($path)
|
||||
{
|
||||
return self::is_readable($path,2);
|
||||
return self::is_readable($path,self::WRITABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -790,7 +886,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*/
|
||||
static function is_executable($path)
|
||||
{
|
||||
return self::is_readable($path,1);
|
||||
return self::is_readable($path,self::EXECUTABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -811,7 +907,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*
|
||||
* @param string $path string with path
|
||||
* @param int $rights=null rights to set, or null to delete the entry
|
||||
* @param int/boolean $owner=null owner for whom to set the rights, null for the current user, or false to delete all rights for $path
|
||||
* @param int|boolean $owner=null owner for whom to set the rights, null for the current user, or false to delete all rights for $path
|
||||
* @return boolean true if acl is set/deleted, false on error
|
||||
*/
|
||||
static function eacl($url,$rights=null,$owner=null)
|
||||
@ -825,7 +921,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* Calls itself recursive, to get the parent directories
|
||||
*
|
||||
* @param string $path
|
||||
* @return array/boolean array with array('path'=>$path,'owner'=>$owner,'rights'=>$rights) or false if $path not found
|
||||
* @return array|boolean array with array('path'=>$path,'owner'=>$owner,'rights'=>$rights) or false if $path not found
|
||||
*/
|
||||
static function get_eacl($path)
|
||||
{
|
||||
@ -836,7 +932,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* Store properties for a single ressource (file or dir)
|
||||
*
|
||||
* @param string $path string with path
|
||||
* @param array $props array or array with values for keys 'name', 'ns', 'val' (null to delete the prop)
|
||||
* @param array $props array of array with values for keys 'name', 'ns', 'val' (null to delete the prop)
|
||||
* @return boolean true if props are updated, false otherwise (eg. ressource not found)
|
||||
*/
|
||||
static function proppatch($path,array $props)
|
||||
@ -874,7 +970,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
/**
|
||||
* Convert a symbolic mode string or octal mode to an integer
|
||||
*
|
||||
* @param string/int $set comma separated mode string to set [ugo]+[+=-]+[rwx]+
|
||||
* @param string|int $set comma separated mode string to set [ugo]+[+=-]+[rwx]+
|
||||
* @param int $mode=0 current mode of the file, necessary for +/- operation
|
||||
* @return int
|
||||
*/
|
||||
@ -1022,6 +1118,10 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
$img = $GLOBALS['egw']->common->image('etemplate',$icon='mime'.$size.'_unknown');
|
||||
}
|
||||
if ($et_image === 'url')
|
||||
{
|
||||
return $img;
|
||||
}
|
||||
if ($et_image)
|
||||
{
|
||||
return 'etemplate/'.$icon;
|
||||
@ -1030,7 +1130,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Human readable size values in k or M
|
||||
* Human readable size values in k, M or G
|
||||
*
|
||||
* @param int $size
|
||||
* @return string
|
||||
@ -1039,7 +1139,8 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
if ($size < 1024) return $size;
|
||||
if ($size < 1024*1024) return sprintf('%3.1lfk',(float)$size/1024);
|
||||
return sprintf('%3.1lfM',(float)$size/(1024*1024));
|
||||
if ($size < 1024*1024*1024) return sprintf('%3.1lfM',(float)$size/(1024*1024));
|
||||
return sprintf('%3.1lfG',(float)$size/(1024*1024*1024));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1059,8 +1160,10 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
/**
|
||||
* Get the directory / parent of a given path or url(!), return false for '/'!
|
||||
*
|
||||
* Also works around PHP under Windows returning dirname('/something') === '\\', which is NOT understood by EGroupware's VFS!
|
||||
*
|
||||
* @param string $path path or url
|
||||
* @return string/boolean parent or false if there's none ($path == '/')
|
||||
* @return string|boolean parent or false if there's none ($path == '/')
|
||||
*/
|
||||
static function dirname($url)
|
||||
{
|
||||
@ -1169,7 +1272,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
}
|
||||
// we do NOT need to encode % itself, as our path are already url encoded, with the exception of ' ' and '+'
|
||||
// we urlencode double quotes '"', as that fixes many problems in html markup
|
||||
return '/webdav.php'.strtr($path,array('+' => '%2B',' ' => '%20','"' => '%22'));
|
||||
return '/webdav.php'.strtr($path,array('+' => '%2B',' ' => '%20','"' => '%22')).($force_download ? '?download' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1337,6 +1440,35 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
return self::$lock_cache[$path] = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get backend specific information (data and etemplate), to integrate as tab in filemanagers settings dialog
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $content=null
|
||||
* @return array|boolean array with values for keys 'data','etemplate','name','label','help' or false if not supported by backend
|
||||
*/
|
||||
static function getExtraInfo($path,array $content=null)
|
||||
{
|
||||
$extra = array();
|
||||
if (($extra_info = self::_call_on_backend('extra_info',array($path,$content),true))) // true = fail silent if backend does NOT support it
|
||||
{
|
||||
$extra[] = $extra_info;
|
||||
}
|
||||
|
||||
if (($vfs_extra = $GLOBALS['egw']->hooks->process(array(
|
||||
'location' => 'vfs_extra',
|
||||
'path' => $path,
|
||||
'content' => $content,
|
||||
))))
|
||||
{
|
||||
foreach($vfs_extra as $app => $data)
|
||||
{
|
||||
$extra = $extra ? array_merge($extra, $data) : $data;
|
||||
}
|
||||
}
|
||||
return $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapps entries of applications to a path for the locking
|
||||
*
|
||||
@ -1412,6 +1544,234 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
self::$db = isset($GLOBALS['egw_setup']->db) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db;
|
||||
self::$lock_cache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to the thumbnail of the given file. The thumbnail may simply
|
||||
* be the mime-type icon, or - if activated - the preview with the given thsize.
|
||||
*
|
||||
* @param string $file name of the file
|
||||
* @param int $thsize the size of the preview - false if the default should be used.
|
||||
* @param string $mime if you already know the mime type of the file, you can supply
|
||||
* it here. Otherwise supply "false".
|
||||
*/
|
||||
public static function thumbnail_url($file, $thsize = false, $mime = false)
|
||||
{
|
||||
// Retrive the mime-type of the file
|
||||
if (!$mime)
|
||||
{
|
||||
$mime = egw_vfs::mime_content_type($file);
|
||||
}
|
||||
|
||||
$image = "";
|
||||
|
||||
// Seperate the mime type into the primary and the secondary part
|
||||
list($mime_main, $mime_sub) = explode('/', $mime);
|
||||
|
||||
if ($mime_main == 'egw')
|
||||
{
|
||||
$image = $GLOBALS['egw']->common->image($mime_sub, 'navbar');
|
||||
}
|
||||
else if ($file && $mime_main == 'image' && in_array($mime_sub, array('png','jpeg','jpg','gif','bmp')) &&
|
||||
(string)$GLOBALS['egw_info']['server']['link_list_thumbnail'] != '0' &&
|
||||
(string)$GLOBALS['egw_info']['user']['preferences']['common']['link_list_thumbnail'] != '0' &&
|
||||
(!is_array($value) && ($stat = egw_vfs::stat($file)) ? $stat['size'] : $value['size']) < 1500000)
|
||||
{
|
||||
if (substr($file, 0, 6) == '/apps/')
|
||||
{
|
||||
$file = parse_url(egw_vfs::resolve_url_symlinks($path), PHP_URL_PATH);
|
||||
}
|
||||
|
||||
//Assemble the thumbnail parameters
|
||||
$thparams = array();
|
||||
$thparams['path'] = $file;
|
||||
if ($thsize)
|
||||
{
|
||||
$thparams['thsize'] = $thsize;
|
||||
}
|
||||
$image = $GLOBALS['egw']->link('/etemplate/thumbnail.php', $thparams);
|
||||
}
|
||||
else
|
||||
{
|
||||
list($app, $name) = explode("/", egw_vfs::mime_icon($mime), 2);
|
||||
$image = $GLOBALS['egw']->common->image($app, $name);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured start directory for the current user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static public function get_home_dir()
|
||||
{
|
||||
$start = '/home/'.$GLOBALS['egw_info']['user']['account_lid'];
|
||||
|
||||
// check if user specified a valid startpath in his prefs --> use it
|
||||
if (($path = $GLOBALS['egw_info']['user']['preferences']['filemanager']['startfolder']) &&
|
||||
$path[0] == '/' && egw_vfs::is_dir($path) && egw_vfs::check_access($path, egw_vfs::READABLE))
|
||||
{
|
||||
$start = $path;
|
||||
}
|
||||
return $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the files given in $src to $dst.
|
||||
*
|
||||
* @param array $src contains the source file
|
||||
* @param string $dst is the destination directory
|
||||
*/
|
||||
static public function copy_files(array $src, $dst, &$errs, array &$copied)
|
||||
{
|
||||
if (self::is_dir($dst))
|
||||
{
|
||||
foreach ($src as $file)
|
||||
{
|
||||
// Check whether the file has already been copied - prevents from
|
||||
// recursion
|
||||
if (!in_array($file, $copied))
|
||||
{
|
||||
// Calculate the target filename
|
||||
$target = egw_vfs::concat($dst, egw_vfs::basename($file));
|
||||
|
||||
if (self::is_dir($file))
|
||||
{
|
||||
if ($file !== $target)
|
||||
{
|
||||
// Create the target directory
|
||||
egw_vfs::mkdir($target,null,STREAM_MKDIR_RECURSIVE);
|
||||
|
||||
$files = egw_vfs::find($file, array(
|
||||
"hidden" => true
|
||||
));
|
||||
|
||||
$copied[] = $file;
|
||||
$copied[] = $target; // < newly created folder must not be copied again!
|
||||
if (egw_vfs::copy_files(egw_vfs::find($file), $target,
|
||||
$errs, $copied))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$errs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy a single file - check whether the file should be
|
||||
// copied onto itself.
|
||||
// TODO: Check whether target file already exists and give
|
||||
// return those files so that a dialog might be displayed
|
||||
// on the client side which lets the user decide.
|
||||
if ($target !== $file && egw_vfs::copy($file, $target))
|
||||
{
|
||||
$copied[] = $file;
|
||||
}
|
||||
else
|
||||
{
|
||||
$errs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errs == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the files given in src to dst
|
||||
*/
|
||||
static public function move_files(array $src, $dst, &$errs, array &$moved)
|
||||
{
|
||||
if (egw_vfs::is_dir($dst))
|
||||
{
|
||||
foreach($src as $file)
|
||||
{
|
||||
$target = egw_vfs::concat($dst, egw_vfs::basename($file));
|
||||
|
||||
if ($file != $target && egw_vfs::rename($file, $target))
|
||||
{
|
||||
$moved[] = $file;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$errs;
|
||||
}
|
||||
}
|
||||
|
||||
return $errs == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an uploaded file into the vfs, optionally set some properties (eg. comment or other cf's)
|
||||
*
|
||||
* Treat copying incl. properties as atomar operation in respect of notifications (one notification about an added file).
|
||||
*
|
||||
* @param array|string $src path to uploaded file or etemplate file array (value for key 'tmp_name')
|
||||
* @param string $target path or directory to copy uploaded file
|
||||
* @param array|string $props=null array with properties (name => value pairs, eg. 'comment' => 'FooBar','#cfname' => 'something'),
|
||||
* array as for proppatch (array of array with values for keys 'name', 'val' and optional 'ns') or string with comment
|
||||
* @param boolean $check_is_uploaded_file=true should method perform an is_uploaded_file check, default yes
|
||||
* @return boolean|array stat array on success, false on error
|
||||
*/
|
||||
static public function copy_uploaded($src,$target,$props=null,$check_is_uploaded_file=true)
|
||||
{
|
||||
$tmp_name = is_array($src) ? $src['tmp_name'] : $src;
|
||||
|
||||
if (self::stat($target) && self::is_dir($target))
|
||||
{
|
||||
$target = self::concat($target, self::encodePathComponent(is_array($src) ? $src['name'] : basename($tmp_name)));
|
||||
}
|
||||
if ($check_is_uploaded_file && !is_uploaded_file($tmp_name))
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($tmp_name, $target, ".array2string($props).",$check_is_uploaded_file) returning FALSE !is_uploaded_file()");
|
||||
return false;
|
||||
}
|
||||
if (!(self::is_writable($target) || self::is_writable(self::dirname($target))))
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($tmp_name, $target, ".array2string($props).",$check_is_uploaded_file) returning FALSE !writable");
|
||||
return false;
|
||||
}
|
||||
if ($props)
|
||||
{
|
||||
if (!is_array($props)) $props = array(array('name' => 'comment','val' => $props));
|
||||
|
||||
// if $props is name => value pairs, convert it to internal array or array with values for keys 'name', 'val' and optional 'ns'
|
||||
if (!isset($props[0]))
|
||||
{
|
||||
foreach($props as $name => $val)
|
||||
{
|
||||
if (($name == 'comment' || $name[0] == '#') && $val) // only copy 'comment' and cfs
|
||||
{
|
||||
$vfs_props[] = array(
|
||||
'name' => $name,
|
||||
'val' => $val,
|
||||
);
|
||||
}
|
||||
}
|
||||
$props = $vfs_props;
|
||||
}
|
||||
}
|
||||
if ($props)
|
||||
{
|
||||
// set props before copying the file, so notifications already contain them
|
||||
if (!self::stat($target))
|
||||
{
|
||||
self::touch($target); // create empty file, to be able to attach properties
|
||||
self::$treat_as_new = true; // notify as new
|
||||
}
|
||||
self::proppatch($target, $props);
|
||||
}
|
||||
$ret = copy($tmp_name,self::PREFIX.$target) ? self::stat($target) : false;
|
||||
if (self::LOG_LEVEL > 1 || !$ret && self::LOG_LEVEL) error_log(__METHOD__."($tmp_name, $target, ".array2string($props).") returning ".array2string($ret));
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
egw_vfs::init_static();
|
||||
|
@ -30,7 +30,6 @@
|
||||
* The stream wrapper interface is according to the docu on php.net
|
||||
*
|
||||
* @link http://www.php.net/manual/en/function.stream-wrapper-register.php
|
||||
* @ToDo versioning
|
||||
*/
|
||||
class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
@ -80,10 +79,6 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
const LOG_LEVEL = 1;
|
||||
|
||||
/**
|
||||
* We do versioning AND store the content in the db, NOT YET IMPLEMENTED
|
||||
*/
|
||||
const VERSIONING = 0;
|
||||
/**
|
||||
* We store the content in the DB (no versioning)
|
||||
*/
|
||||
@ -142,7 +137,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* @var array $path => info-array pairs
|
||||
*/
|
||||
static private $stat_cache = array();
|
||||
static protected $stat_cache = array();
|
||||
/**
|
||||
* Reference to the PDO object we use
|
||||
*
|
||||
@ -165,6 +160,21 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static public $extra_columns = ',fs_link';
|
||||
|
||||
/**
|
||||
* Clears our stat-cache
|
||||
*
|
||||
* Normaly not necessary, as it is automatically cleared/updated, UNLESS egw_vfs::$user changes!
|
||||
*
|
||||
* @param string $path='/'
|
||||
*/
|
||||
public static function clearstatcache($path='/')
|
||||
{
|
||||
//error_log(__METHOD__."('$path')");
|
||||
self::$stat_cache = array();
|
||||
|
||||
$GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME,self::$extended_acl = null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called immediately after your stream object is created.
|
||||
*
|
||||
@ -174,10 +184,13 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* - STREAM_USE_PATH If path is relative, search for the resource using the include_path.
|
||||
* - STREAM_REPORT_ERRORS If this flag is set, you are responsible for raising errors using trigger_error() during opening of the stream.
|
||||
* If this flag is not set, you should not raise any errors.
|
||||
* @param string $opened_path full path of the file/resource, if the open was successfull and STREAM_USE_PATH was set
|
||||
* @param string &$opened_path full path of the file/resource, if the open was successfull and STREAM_USE_PATH was set
|
||||
* @param array $overwrite_new=null if set create new file with values overwriten by the given ones
|
||||
* @param string $class=__CLASS__ class to use to call static methods, eg url_stat (workaround for no late static binding in php < 5.3)
|
||||
* @todo remove $class parameter and use static::url_stat() once we require PHP5.3!
|
||||
* @return boolean true if the ressource was opened successful, otherwise false
|
||||
*/
|
||||
function stream_open ( $url, $mode, $options, &$opened_path )
|
||||
function stream_open ( $url, $mode, $options, &$opened_path, array $overwrite_new=null, $class=__CLASS__ )
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)");
|
||||
|
||||
@ -189,16 +202,16 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
$this->opened_mode = $mode = str_replace('b','',$mode); // we are always binary, like every Linux system
|
||||
$this->opened_stream = null;
|
||||
|
||||
if (!($stat = self::url_stat($path,STREAM_URL_STAT_QUIET)) || $mode[0] == 'x') // file not found or file should NOT exist
|
||||
if (!is_null($overwrite_new) || !($stat = call_user_func(array($class,'url_stat'),$path,STREAM_URL_STAT_QUIET)) || $mode[0] == 'x') // file not found or file should NOT exist
|
||||
{
|
||||
if ($mode[0] == 'r' || // does $mode require the file to exist (r,r+)
|
||||
$mode[0] == 'x' || // or file should not exist, but does
|
||||
!($dir_stat=self::url_stat($dir,STREAM_URL_STAT_QUIET)) || // or parent dir does not exist create it
|
||||
!($dir_stat=call_user_func(array($class,'url_stat'),$dir,STREAM_URL_STAT_QUIET)) || // or parent dir does not exist create it
|
||||
!egw_vfs::check_access($dir,egw_vfs::WRITABLE,$dir_stat)) // or we are not allowed to create it
|
||||
{
|
||||
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))
|
||||
if (($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error(__METHOD__."($url,$mode,$options) file does not exist or can not be created!",E_USER_WARNING);
|
||||
}
|
||||
@ -207,8 +220,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
// new file --> create it in the DB
|
||||
$new_file = true;
|
||||
$query = 'INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size'.
|
||||
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size)';
|
||||
$query = 'INSERT INTO '.self::TABLE.' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size,fs_active'.
|
||||
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_active)';
|
||||
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
|
||||
$stmt = self::$pdo->prepare($query);
|
||||
$values = array(
|
||||
@ -225,7 +238,9 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
'fs_creator' => egw_vfs::$user,
|
||||
'fs_mime' => 'application/octet-stream', // required NOT NULL!
|
||||
'fs_size' => 0,
|
||||
'fs_active' => self::_pdo_boolean(true),
|
||||
);
|
||||
if ($overwrite_new) $values = array_merge($values,$overwrite_new);
|
||||
if (!$stmt->execute($values) || !($this->opened_fs_id = self::$pdo->lastInsertId('egw_sqlfs_fs_id_seq')))
|
||||
{
|
||||
$this->opened_stream = $this->opened_path = $this->opened_mode = null;
|
||||
@ -245,6 +260,17 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
self::mkdir_recursive($fs_dir,0700,true);
|
||||
}
|
||||
}
|
||||
// check if opend file is a directory
|
||||
elseif($stat && ($stat['mode'] & self::MODE_DIR) == self::MODE_DIR)
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) Is a directory!");
|
||||
if (($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error(__METHOD__."($url,$mode,$options) Is a directory!",E_USER_WARNING);
|
||||
}
|
||||
$this->opened_stream = $this->opened_path = $this->opened_mode = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($mode == 'r' && !egw_vfs::check_access($url,egw_vfs::READABLE ,$stat) ||// we are not allowed to read
|
||||
@ -253,7 +279,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
self::_remove_password($url);
|
||||
$op = $mode == 'r' ? 'read' : 'edited';
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file can not be $op!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
if (($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error(__METHOD__."($url,$mode,$options) file can not be $op!",E_USER_WARNING);
|
||||
}
|
||||
@ -514,13 +540,13 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* @param string $url
|
||||
* @return boolean TRUE on success or FALSE on failure
|
||||
*/
|
||||
static function unlink ( $url )
|
||||
static function unlink ( $url, $parent_stat=null )
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!($stat = self::url_stat($path,STREAM_URL_STAT_LINK)) || !egw_vfs::check_access(egw_vfs::dirname($path),egw_vfs::WRITABLE))
|
||||
if (!($stat = self::url_stat($path,STREAM_URL_STAT_LINK)) || !egw_vfs::check_access(egw_vfs::dirname($path),egw_vfs::WRITABLE, $parent_stat))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
|
||||
@ -569,12 +595,14 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url_from,$url_to)");
|
||||
|
||||
$path_from = parse_url($url_from,PHP_URL_PATH);
|
||||
$from_dir = egw_vfs::dirname($path_from);
|
||||
$path_to = parse_url($url_to,PHP_URL_PATH);
|
||||
$to_dir = egw_vfs::dirname($path_to);
|
||||
$operation = self::url2operation($url_from);
|
||||
|
||||
// we have to use array($class,'url_stat'), as $class.'::url_stat' requires PHP 5.2.3 and we currently only require 5.2+
|
||||
if (!($from_stat = call_user_func(array($class,'url_stat'),$path_from,0)) || !egw_vfs::check_access(dirname($path_from),egw_vfs::WRITABLE))
|
||||
if (!($from_stat = call_user_func(array($class,'url_stat'),$path_from,0)) ||
|
||||
!egw_vfs::check_access($from_dir,egw_vfs::WRITABLE,$from_dir_stat = call_user_func(array($class,'url_stat'),$from_dir,0)))
|
||||
{
|
||||
self::_remove_password($url_from);
|
||||
self::_remove_password($url_to);
|
||||
@ -609,11 +637,12 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
unset(self::$stat_cache[$path_from]);
|
||||
unset(self::$stat_cache[$path_to]);
|
||||
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_dir=:fs_dir,fs_name=:fs_name WHERE fs_id=:fs_id');
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_dir=:fs_dir,fs_name=:fs_name WHERE fs_dir=:old_dir AND fs_name=:old_name');
|
||||
$ok = $stmt->execute(array(
|
||||
'fs_dir' => $to_dir_stat['ino'],
|
||||
'fs_name' => egw_vfs::basename($path_to),
|
||||
'fs_id' => $from_stat['ino'],
|
||||
'fs_dir' => $to_dir_stat['ino'],
|
||||
'fs_name' => egw_vfs::basename($path_to),
|
||||
'old_dir' => $from_dir_stat['ino'],
|
||||
'old_name' => $from_stat['name'],
|
||||
));
|
||||
unset($stmt);
|
||||
|
||||
@ -635,7 +664,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
/**
|
||||
* due to problems with recursive directory creation, we have our own here
|
||||
*/
|
||||
function mkdir_recursive($pathname, $mode, $depth=0)
|
||||
private static function mkdir_recursive($pathname, $mode, $depth=0)
|
||||
{
|
||||
$maxdepth=10;
|
||||
$depth2propagate = (int)$depth + 1;
|
||||
@ -665,7 +694,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) already exist!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
if (!($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
//throw new Exception(__METHOD__."('$url',$mode,$options) already exist!");
|
||||
trigger_error(__METHOD__."('$url',$mode,$options) already exist!",E_USER_WARNING);
|
||||
@ -691,7 +720,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) permission denied!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
if (!($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error(__METHOD__."('$url',$mode,$options) permission denied!",E_USER_WARNING);
|
||||
}
|
||||
@ -756,7 +785,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
$err_msg = __METHOD__."($url,$options) ".(!$stat ? 'not found!' :
|
||||
($stat['mime'] != self::DIR_MIME_TYPE ? 'not a directory!' : 'permission denied!'));
|
||||
if (self::LOG_LEVEL) error_log($err_msg);
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
if (!($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error($err_msg,E_USER_WARNING);
|
||||
}
|
||||
@ -768,7 +797,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) dir is not empty!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
if (!($options & STREAM_REPORT_ERRORS))
|
||||
{
|
||||
trigger_error(__METHOD__."('$url',$options) dir is not empty!",E_USER_WARNING);
|
||||
}
|
||||
@ -971,7 +1000,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
$this->opened_dir = array();
|
||||
$query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.self::$extra_columns.
|
||||
' FROM '.self::TABLE." WHERE fs_dir=? ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC";
|
||||
' FROM '.self::TABLE.' WHERE fs_dir=? AND fs_active='.self::_pdo_boolean(true).
|
||||
" ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC";
|
||||
//if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
|
||||
if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__."($url,$options)".' */ '.$query;
|
||||
|
||||
@ -1020,7 +1050,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static function url_stat ( $url, $flags, $eacl_access=null )
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags)");
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags,$eacl_access)");
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
@ -1044,7 +1074,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
self::_pdo();
|
||||
}
|
||||
$base_query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.self::$extra_columns.
|
||||
' FROM '.self::TABLE.' WHERE fs_name'.self::$case_sensitive_equal.'? AND fs_dir=';
|
||||
' FROM '.self::TABLE.' WHERE fs_active='.self::_pdo_boolean(true).
|
||||
' AND fs_name'.self::$case_sensitive_equal.'? AND fs_dir=';
|
||||
$parts = explode('/',$path);
|
||||
|
||||
// if we have extendes acl access to the url, we dont need and can NOT include the sql for the readable check
|
||||
@ -1075,7 +1106,8 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
// we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)
|
||||
return self::$stat_cache[$path] = false;
|
||||
}
|
||||
$query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name);
|
||||
$query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_active='.
|
||||
self::_pdo_boolean(true).' AND fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name);
|
||||
|
||||
// if we are not root AND have no extended acl access, we need to make sure the user has the right to tranverse all parent directories (read-rights)
|
||||
if (!egw_vfs::$is_root && !$eacl_access)
|
||||
@ -1118,7 +1150,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function _sql_readable()
|
||||
protected function _sql_readable()
|
||||
{
|
||||
static $sql_read_acl;
|
||||
|
||||
@ -1283,7 +1315,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* Read the extended acl via acl::get_grants('sqlfs')
|
||||
*
|
||||
*/
|
||||
private static function _read_extended_acl()
|
||||
static protected function _read_extended_acl()
|
||||
{
|
||||
if ((self::$extended_acl = $GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME)) != false)
|
||||
{
|
||||
@ -1385,7 +1417,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
function get_eacl($path)
|
||||
{
|
||||
if (!($stat = self::url_stat($path,STREAM_URL_STAT_QUIET)))
|
||||
if (!($stat = self::url_stat($_path=$path,STREAM_URL_STAT_QUIET)))
|
||||
{
|
||||
error_log(__METHOD__.__LINE__.' '.array2string($path).' not found!');
|
||||
return false; // not found
|
||||
@ -1402,11 +1434,11 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
if (($path = egw_vfs::dirname($path)))
|
||||
{
|
||||
return array_merge((array)self::get_eacl($path),$eacls);
|
||||
$eacls = array_merge((array)self::get_eacl($path),$eacls);
|
||||
}
|
||||
// sort by length descending, to show precedence
|
||||
usort($eacls,create_function('$a,$b','return strlen($b["path"])-strlen($a["path"]);'));
|
||||
|
||||
//error_log(__METHOD__."('$_path') returning ".array2string($eacls));
|
||||
return $eacls;
|
||||
}
|
||||
|
||||
@ -1486,7 +1518,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* @param array $info
|
||||
* @return array
|
||||
*/
|
||||
static private function _vfsinfo2stat($info)
|
||||
static protected function _vfsinfo2stat($info)
|
||||
{
|
||||
$stat = array(
|
||||
'ino' => $info['fs_id'],
|
||||
@ -1563,7 +1595,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
self::$pdo = new PDO($dsn,$egw_db->User,'$egw_db->Password');
|
||||
}
|
||||
// set client charset of the connection
|
||||
$charset = $GLOBALS['egw']->translation->charset();
|
||||
$charset = translation::charset();
|
||||
switch(self::$pdo_type)
|
||||
{
|
||||
case 'mysql':
|
||||
@ -1586,7 +1618,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* @param mixed $time
|
||||
* @return string Y-m-d H:i:s
|
||||
*/
|
||||
static private function _pdo_timestamp($time)
|
||||
static protected function _pdo_timestamp($time)
|
||||
{
|
||||
if (is_numeric($time))
|
||||
{
|
||||
@ -1668,7 +1700,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* @param string &$url
|
||||
*/
|
||||
static private function _remove_password(&$url)
|
||||
static protected function _remove_password(&$url)
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
|
||||
@ -1711,7 +1743,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
* Store properties for a single ressource (file or dir)
|
||||
*
|
||||
* @param string|int $path string with path or integer fs_id
|
||||
* @param array $props array or array with values for keys 'name', 'ns', 'val' (null to delete the prop)
|
||||
* @param array $props array of array with values for keys 'name', 'ns', 'val' (null to delete the prop)
|
||||
* @return boolean true if props are updated, false otherwise (eg. ressource not found)
|
||||
*/
|
||||
static function proppatch($path,array $props)
|
||||
@ -1814,7 +1846,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
if (!is_array($path_ids))
|
||||
{
|
||||
$props = $props[$row['fs_id']];
|
||||
$props = $props[$row['fs_id']] ? $props[$row['fs_id']] : array(); // return empty array for no props
|
||||
}
|
||||
elseif ($props && isset($stat)) // need to map fs_id's to pathes
|
||||
{
|
||||
|
@ -62,16 +62,9 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
// special treatment for litmus compliance test
|
||||
// reply on its identifier header
|
||||
// not needed for the test itself but eases debugging
|
||||
if (function_exists('apache_request_headers'))
|
||||
{
|
||||
foreach (apache_request_headers() as $key => $value)
|
||||
{
|
||||
if (stristr($key, 'litmus'))
|
||||
{
|
||||
error_log("Litmus test $value");
|
||||
header('X-Litmus-reply: '.$value);
|
||||
}
|
||||
}
|
||||
if (isset($this->_SERVER['HTTP_X_LITMUS'])) {
|
||||
error_log("Litmus test ".$this->_SERVER['HTTP_X_LITMUS']);
|
||||
header("X-Litmus-reply: ".$this->_SERVER['HTTP_X_LITMUS']);
|
||||
}
|
||||
// let the base class do all the work
|
||||
HTTP_WebDAV_Server::ServeRequest();
|
||||
@ -135,7 +128,28 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
$source = $this->base .$options["path"];
|
||||
if (!file_exists($source)) return "404 Not found";
|
||||
|
||||
if (is_dir($source)) { // resource is a collection
|
||||
switch ($options["depth"]) {
|
||||
case "infinity": // valid
|
||||
break;
|
||||
case "0": // valid for COPY only
|
||||
if ($del) { // MOVE?
|
||||
return "400 Bad request";
|
||||
}
|
||||
break;
|
||||
case "1": // invalid for both COPY and MOVE
|
||||
default:
|
||||
return "400 Bad request";
|
||||
}
|
||||
}
|
||||
|
||||
$dest = $this->base . $options["dest"];
|
||||
$destdir = dirname($dest);
|
||||
|
||||
if (!file_exists($destdir) || !is_dir($destdir)) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
|
||||
$new = !file_exists($dest);
|
||||
$existing_col = false;
|
||||
|
||||
@ -165,11 +179,6 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir($source) && ($options["depth"] != "infinity")) {
|
||||
// RFC 2518 Section 9.2, last paragraph
|
||||
return "400 Bad request";
|
||||
}
|
||||
|
||||
if ($del) {
|
||||
if (!rename($source, $dest)) {
|
||||
return "500 Internal server error";
|
||||
@ -189,9 +198,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
mysql_query($query);
|
||||
*/
|
||||
} else {
|
||||
if (is_dir($source)) {
|
||||
$files = System::find($source);
|
||||
$files = array_reverse($files);
|
||||
if (is_dir($source) && $options['depth'] == 'infinity') {
|
||||
$files = egw_vfs::find($source,array('depth' => true,'url' => true)); // depth=true: return dirs first, url=true: allow urls!
|
||||
} else {
|
||||
$files = array($source);
|
||||
}
|
||||
@ -243,6 +251,9 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
*/
|
||||
function fileinfo($path)
|
||||
{
|
||||
// internally we require some url-encoding, as vfs_stream_wrapper uses URL's internally
|
||||
$path = str_replace(array('#','?'),array('%23','%3F'),$path);
|
||||
|
||||
//error_log(__METHOD__."($path)");
|
||||
// map URI path to filesystem path
|
||||
$fspath = $this->base . $path;
|
||||
@ -251,15 +262,24 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
$info = array();
|
||||
// TODO remove slash append code when base class is able to do it itself
|
||||
$info['path'] = is_dir($fspath) ? $this->_slashify($path) : $path;
|
||||
|
||||
// remove all urlencoding we need internally in EGw, HTTP_WebDAV_Server will add it's own!
|
||||
// rawurldecode does NOT touch +
|
||||
$info['path'] = rawurldecode($info['path']);
|
||||
|
||||
$info['props'] = array();
|
||||
|
||||
// no special beautified displayname here ...
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('displayname', egw_vfs::basename(self::_unslashify($path)));
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('displayname', egw_vfs::basename(self::_unslashify($info['path'])));
|
||||
|
||||
// creation and modification time
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('creationdate', filectime($fspath));
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getlastmodified', filemtime($fspath));
|
||||
|
||||
// Microsoft extensions: last access time and 'hidden' status
|
||||
$info["props"][] = HTTP_WebDAV_Server::mkprop("lastaccessed", fileatime($fspath));
|
||||
$info["props"][] = HTTP_WebDAV_Server::mkprop("ishidden", egw_vfs::is_hidden($fspath));
|
||||
|
||||
// type and size (caller already made sure that path exists)
|
||||
if (is_dir($fspath)) {
|
||||
// directory (WebDAV collection)
|
||||
@ -292,7 +312,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
*/
|
||||
// ToDo: etag from inode and modification time
|
||||
|
||||
//error_log(__METHOD__."($path) info=".print_r($info,true));
|
||||
//error_log(__METHOD__."($path) info=".array2string($info));
|
||||
return $info;
|
||||
}
|
||||
|
||||
@ -395,6 +415,28 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
return egw_vfs::mime_content_type($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if path is readable by current user
|
||||
*
|
||||
* @param string $fspath
|
||||
* @return boolean
|
||||
*/
|
||||
function _is_readable($fspath)
|
||||
{
|
||||
return egw_vfs::is_readable($fspath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if path is writable by current user
|
||||
*
|
||||
* @param string $fspath
|
||||
* @return boolean
|
||||
*/
|
||||
function _is_writable($fspath)
|
||||
{
|
||||
return egw_vfs::is_writable($fspath);
|
||||
}
|
||||
|
||||
/**
|
||||
* PROPPATCH method handler
|
||||
*
|
||||
@ -527,7 +569,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
{
|
||||
return egw_vfs::checkLock($path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GET method handler for directories
|
||||
*
|
||||
@ -543,4 +585,45 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
||||
|
||||
parent::GetDir($fspath, $options);
|
||||
}
|
||||
|
||||
private $force_download = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Reimplement to add a Content-Disposition header, if ?download is appended to the REQUEST_URI
|
||||
*/
|
||||
function __construct()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download')))
|
||||
{
|
||||
$_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,$this->force_download);
|
||||
}
|
||||
parent::HTTP_WebDAV_Server();
|
||||
}
|
||||
|
||||
/**
|
||||
* GET method handler
|
||||
*
|
||||
* Reimplement to add a Content-Disposition header, if ?download is appended to the REQUEST_URI
|
||||
*
|
||||
* @param array parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function GET(&$options)
|
||||
{
|
||||
if (($ok = parent::GET($options)) && $this->force_download)
|
||||
{
|
||||
if(html::$user_agent == 'msie') // && self::$ua_version == '5.5')
|
||||
{
|
||||
$attachment = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$attachment = ' attachment;';
|
||||
}
|
||||
header('Content-disposition:'.$attachment.' filename="'.egw_vfs::basename($options['path']).'"');
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
}
|
19
webdav.php
19
webdav.php
@ -32,26 +32,11 @@ function check_access(&$account)
|
||||
return egw_digest_auth::autocreate_session_callback($account);
|
||||
}
|
||||
|
||||
// if we are called with a /apps/$app path, use that $app as currentapp, to not require filemanager rights for the links
|
||||
$parts = explode('/',isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : $_SERVER['ORIG_PATH_INFO']);
|
||||
//error_log("webdav: explode".print_r($parts,true));
|
||||
if(count($parts) == 1)
|
||||
{
|
||||
error_log(__METHOD__. "Malformed Url: missing slash:\n".$_SERVER['SERVER_NAME']."\n PATH_INFO:".$_SERVER['PATH_INFO'].
|
||||
"\n REQUEST_URI".$_SERVER['REQUEST_URI']."\n ORIG_SCRIPT_NAME:".$_SERVER['ORIG_SCRIPT_NAME'].
|
||||
"\n REMOTE_ADDR:".$_SERVER['REMOTE_ADDR']."\n PATH_INFO:".$_SERVER['PATH_INFO']."\n HTTP_USER_AGENT:".$_SERVER['HTTP_USER_AGENT']) ;
|
||||
header("HTTP/1.1 501 Not implemented");
|
||||
header("X-WebDAV-Status: 501 Not implemented", true);
|
||||
exit;
|
||||
}
|
||||
|
||||
$app = count($parts) > 3 && $parts[1] == 'apps' ? $parts[2] : 'filemanager';
|
||||
|
||||
$GLOBALS['egw_info'] = array(
|
||||
'flags' => array(
|
||||
'disable_Template_class' => True,
|
||||
'noheader' => True,
|
||||
'currentapp' => $app,
|
||||
'currentapp' => preg_match('|/webdav.php/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager',
|
||||
'autocreate_session_callback' => 'check_access',
|
||||
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
|
||||
'auth_realm' => 'EGroupware WebDAV server', // cant use vfs_webdav_server::REALM as autoloading and include path not yet setup!
|
||||
@ -94,4 +79,4 @@ if (strstr($user_agent, 'microsoft-webdav') !== false ||
|
||||
$webdav_server->cnrnd = true;
|
||||
}
|
||||
$webdav_server->ServeRequest();
|
||||
//error_log(sprintf("WebDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime));
|
||||
//error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime));
|
||||
|
Loading…
Reference in New Issue
Block a user