mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-24 17:04:14 +01:00
got links-stream-wrapper working and refactored context handling into a trait
This commit is contained in:
parent
631a0b11d6
commit
2602157032
101
api/src/Vfs.php
101
api/src/Vfs.php
@ -66,33 +66,8 @@ use HTTP_WebDAV_Server;
|
||||
* Vfs::parse_url($url, $component=-1), Vfs::dirname($url) and Vfs::basename($url) work
|
||||
* on urls containing utf-8 characters, which get NOT urlencoded in our VFS!
|
||||
*/
|
||||
class Vfs
|
||||
class Vfs extends Vfs\Base
|
||||
{
|
||||
const PREFIX = 'vfs://default';
|
||||
/**
|
||||
* Scheme / protocol used for this stream-wrapper
|
||||
*/
|
||||
const SCHEME = Vfs\StreamWrapper::SCHEME;
|
||||
/**
|
||||
* Mime type of directories, the old vfs used 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
||||
*/
|
||||
const DIR_MIME_TYPE = Vfs\StreamWrapper::DIR_MIME_TYPE;
|
||||
/**
|
||||
* Readable bit, for dirs traversable
|
||||
*/
|
||||
const READABLE = 4;
|
||||
/**
|
||||
* Writable bit, for dirs delete or create files in that dir
|
||||
*/
|
||||
const WRITABLE = 2;
|
||||
/**
|
||||
* Excecutable bit, here only use to check if user is allowed to search dirs
|
||||
*/
|
||||
const EXECUTABLE = 1;
|
||||
/**
|
||||
* mode-bits, which have to be set for links
|
||||
*/
|
||||
const MODE_LINK = Vfs\StreamWrapper::MODE_LINK;
|
||||
/**
|
||||
* Name of the lock table
|
||||
*/
|
||||
@ -809,6 +784,7 @@ class Vfs
|
||||
* @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 (self::$user)
|
||||
* @return boolean
|
||||
* @todo deprecated or even remove $user parameter and code
|
||||
*/
|
||||
static function check_access($path, $check, $stat=null, $user=null)
|
||||
{
|
||||
@ -855,77 +831,8 @@ class Vfs
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (self::$is_root)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// throw exception if stat array is used insead of path, can be removed soon
|
||||
if (is_array($path))
|
||||
{
|
||||
throw new Exception\WrongParameter('path has to be string, use check_access($path,$check,$stat=null)!');
|
||||
}
|
||||
// if we have no $stat, delegate whole check to vfs stream-wrapper to correctly deal with shares / effective user-ids
|
||||
if (is_null($stat))
|
||||
{
|
||||
if (!isset($vfs)) $vfs = new Vfs\StreamWrapper();
|
||||
//$stat = $vfs->url_stat($path,0);
|
||||
return $vfs->check_access($path, $check);
|
||||
}
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)");
|
||||
|
||||
if (!$stat)
|
||||
{
|
||||
//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 = self::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)
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via other rights!");
|
||||
return true;
|
||||
}
|
||||
if (!isset($user)) $user = self::$user;
|
||||
// check if there's owner access and we are the owner
|
||||
if (($stat['mode'] & ($check << 6)) == ($check << 6) && $stat['uid'] && $stat['uid'] == $user)
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||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)) == ($check << 3) && $stat['gid'])
|
||||
{
|
||||
if (($memberships = $GLOBALS['egw']->accounts->memberships($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;
|
||||
}
|
||||
}
|
||||
// if we check writable and have a readonly mount --> return false, as backends dont know about r/o url parameter
|
||||
if ($check == self::WRITABLE && Vfs\StreamWrapper::url_is_readonly($stat['url']))
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path, check=writable, ...) failed because mount is readonly");
|
||||
return false;
|
||||
}
|
||||
// check backend for extended acls (only if path given)
|
||||
$ret = $path && self::_call_on_backend('check_extended_acl',array(isset($stat['url'])?$stat['url']:$path,$check),true); // true = fail silent if backend does not support
|
||||
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) ".($ret ? 'backend extended acl granted access.' : 'no access!!!'));
|
||||
return $ret;
|
||||
if (!isset($vfs)) $vfs = new Vfs\StreamWrapper();
|
||||
return $vfs->check_access($path, $check, $stat);
|
||||
}
|
||||
|
||||
/**
|
||||
|
120
api/src/Vfs/Base.php
Normal file
120
api/src/Vfs/Base.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware API: VFS - shared base of Vfs class and Vfs-stream-wrapper
|
||||
*
|
||||
* @link https://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) 2008-20 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Vfs;
|
||||
|
||||
use EGroupware\Api\Vfs;
|
||||
|
||||
/**
|
||||
* Shared base of Vfs class and Vfs-stream-wrapper
|
||||
*/
|
||||
class Base
|
||||
{
|
||||
const PREFIX = 'vfs://default';
|
||||
/**
|
||||
* 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';
|
||||
/**
|
||||
* Readable bit, for dirs traversable
|
||||
*/
|
||||
const READABLE = 4;
|
||||
/**
|
||||
* Writable bit, for dirs delete or create files in that dir
|
||||
*/
|
||||
const WRITABLE = 2;
|
||||
/**
|
||||
* Excecutable bit, here only use to check if user is allowed to search dirs
|
||||
*/
|
||||
const EXECUTABLE = 1;
|
||||
/**
|
||||
* mode-bits, which have to be set for links
|
||||
*/
|
||||
const MODE_LINK = 0120000;
|
||||
|
||||
/**
|
||||
* Allow to call methods of the underlying stream wrapper: touch, chmod, chgrp, chown, ...
|
||||
*
|
||||
* We cant use a magic __call() method, as it does not work for static methods!
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $params first param has to be the path, otherwise we can not determine the correct wrapper
|
||||
* @param boolean $fail_silent =false should only false be returned if function is not supported by the backend,
|
||||
* or should an E_USER_WARNING error be triggered (default)
|
||||
* @param int $path_param_key =0 key in params containing the path, default 0
|
||||
* @param boolean $instanciate =false true: instanciate the class to call method $name, false: static call
|
||||
* @return mixed return value of backend or false if function does not exist on backend
|
||||
*/
|
||||
protected static function _call_on_backend($name, array $params, $fail_silent=false, $path_param_key=0, $instanciate=false)
|
||||
{
|
||||
$pathes = $params[$path_param_key];
|
||||
|
||||
$scheme2urls = array();
|
||||
foreach(is_array($pathes) ? $pathes : array($pathes) as $path)
|
||||
{
|
||||
if (!($url = Vfs::resolve_url_symlinks($path,false,false)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$k=(string)Vfs::parse_url($url,PHP_URL_SCHEME);
|
||||
if (!(is_array($scheme2urls[$k]))) $scheme2urls[$k] = array();
|
||||
$scheme2urls[$k][$path] = $url;
|
||||
}
|
||||
$ret = array();
|
||||
foreach($scheme2urls as $scheme => $urls)
|
||||
{
|
||||
if ($scheme)
|
||||
{
|
||||
if (!class_exists($class = Vfs\StreamWrapper::scheme2class($scheme)) || !method_exists($class,$name))
|
||||
{
|
||||
if (!$fail_silent) trigger_error("Can't $name for scheme $scheme!\n",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$callback = [$instanciate ? new $class($url) : $class, $name];
|
||||
if (!is_array($pathes))
|
||||
{
|
||||
$params[$path_param_key] = $url;
|
||||
|
||||
return call_user_func_array($callback, $params);
|
||||
}
|
||||
$params[$path_param_key] = $urls;
|
||||
if (!is_array($r = call_user_func_array($callback, $params)))
|
||||
{
|
||||
return $r;
|
||||
}
|
||||
// we need to re-translate the urls to pathes, as they can eg. contain symlinks
|
||||
foreach($urls as $path => $url)
|
||||
{
|
||||
if (isset($r[$url]) || isset($r[$url=Vfs::parse_url($url,PHP_URL_PATH)]))
|
||||
{
|
||||
$ret[$path] = $r[$url];
|
||||
}
|
||||
}
|
||||
}
|
||||
// call the filesystem specific function (dont allow to use arrays!)
|
||||
elseif(!function_exists($name) || is_array($pathes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$time = null;
|
||||
return $name($url,$time);
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
@ -95,7 +95,7 @@ class StreamWrapper extends LinksParent
|
||||
$access = !($check & Vfs::WRITABLE); // always grant read access to /apps
|
||||
$what = '!$app';
|
||||
}
|
||||
elseif (!self::check_app_rights($app))
|
||||
elseif (!$this->check_app_rights($app))
|
||||
{
|
||||
$access = false; // user has no access to the $app application
|
||||
$what = 'no app-rights';
|
||||
@ -112,8 +112,8 @@ class StreamWrapper extends LinksParent
|
||||
{
|
||||
// vfs & stream-wrapper use posix rights, Api\Link::file_access uses Api\Acl::{EDIT|READ}!
|
||||
$required = $check & Vfs::WRITABLE ? Api\Acl::EDIT : Api\Acl::READ;
|
||||
$access = Api\Link::file_access($app,$id,$required,$rel_path,Vfs::$user);
|
||||
$what = "from Api\Link::file_access('$app',$id,$required,'$rel_path,".Vfs::$user.")";
|
||||
$access = Api\Link::file_access($app, $id, $required, $rel_path, $this->user);
|
||||
$what = "from Api\Link::file_access('$app', $id, $required, '$rel_path,".$this->user.")";
|
||||
}
|
||||
if (self::DEBUG) error_log(__METHOD__."($url,$check) user=".Vfs::$user." ($what) ".($access?"access granted ($app:$id:$rel_path)":'no access!!!'));
|
||||
return $access;
|
||||
@ -125,18 +125,18 @@ class StreamWrapper extends LinksParent
|
||||
* @param string $app
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check_app_rights($app)
|
||||
public function check_app_rights($app)
|
||||
{
|
||||
if ($GLOBALS['egw_info']['user']['account_id'] == Vfs::$user)
|
||||
if ($GLOBALS['egw_info']['user']['account_id'] == $this->user && isset($GLOBALS['egw_info']['user']['apps']))
|
||||
{
|
||||
return isset($GLOBALS['egw_info']['user']['apps'][$app]);
|
||||
}
|
||||
static $user_apps = array();
|
||||
if (!isset($user_apps[Vfs::$user]))
|
||||
if (!isset($user_apps[$this->user]))
|
||||
{
|
||||
$user_apps[Vfs::$user] = $GLOBALS['egw']->acl->get_user_applications(Vfs::$user);
|
||||
$user_apps[$this->user] = $GLOBALS['egw']->acl->get_user_applications($this->user);
|
||||
}
|
||||
return !empty($user_apps[Vfs::$user][$app]);
|
||||
return !empty($user_apps[$this->user][$app]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,7 @@ use EGroupware\Api;
|
||||
*/
|
||||
class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
{
|
||||
use Vfs\UserContext;
|
||||
use Vfs\UserContextTrait;
|
||||
|
||||
/**
|
||||
* Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
||||
@ -206,7 +206,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
if (!$dir || $mode[0] == 'r' || // does $mode require the file to exist (r,r+)
|
||||
$mode[0] == 'x' && $stat || // or file should not exist, but does
|
||||
!($dir_stat=$this->url_stat($dir,STREAM_URL_STAT_QUIET)) || // or parent dir does not exist create it
|
||||
!Vfs::check_access($dir,Vfs::WRITABLE, $dir_stat, $this->user)) // or we are not allowed to create it
|
||||
!$this->check_access($dir,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!");
|
||||
@ -272,8 +272,8 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($mode == 'r' && !Vfs::check_access($url,Vfs::READABLE ,$stat, $this->user) ||// we are not allowed to read
|
||||
$mode != 'r' && !Vfs::check_access($url,Vfs::WRITABLE, $stat, $this->user)) // or edit it
|
||||
if ($mode == 'r' && !$this->check_access($url,Vfs::READABLE , $stat) ||// we are not allowed to read
|
||||
$mode != 'r' && !$this->check_access($url,Vfs::WRITABLE, $stat)) // or edit it
|
||||
{
|
||||
self::_remove_password($url);
|
||||
$op = $mode == 'r' ? 'read' : 'edited';
|
||||
@ -555,7 +555,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
$this->url_stat($dir, STREAM_URL_STAT_LINK);
|
||||
|
||||
if (!$parent_stat || !($stat = $this->url_stat($path,STREAM_URL_STAT_LINK)) ||
|
||||
!$dir || !Vfs::check_access($dir, Vfs::WRITABLE, $parent_stat, $this->user))
|
||||
!$dir || !$this->check_access($dir, Vfs::WRITABLE, $parent_stat))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
|
||||
@ -607,14 +607,14 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
$to_dir = Vfs::dirname($path_to);
|
||||
|
||||
if (!($from_stat = $this->url_stat($path_from, 0)) || !$from_dir ||
|
||||
!Vfs::check_access($from_dir, Vfs::WRITABLE, $from_dir_stat = $this->url_stat($from_dir, 0), $this->user))
|
||||
!$this->check_access($from_dir, Vfs::WRITABLE, $from_dir_stat = $this->url_stat($from_dir, 0)))
|
||||
{
|
||||
self::_remove_password($url_from);
|
||||
self::_remove_password($url_to);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_from permission denied!");
|
||||
return false; // no permission or file does not exist
|
||||
}
|
||||
if (!$to_dir || !Vfs::check_access($to_dir, Vfs::WRITABLE, $to_dir_stat = $this->url_stat($to_dir, 0), $this->user))
|
||||
if (!$to_dir || !$this->check_access($to_dir, Vfs::WRITABLE, $to_dir_stat = $this->url_stat($to_dir, 0)))
|
||||
{
|
||||
self::_remove_password($url_from);
|
||||
self::_remove_password($url_to);
|
||||
@ -730,7 +730,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
}
|
||||
$parent = $this->url_stat($parent_path,0);
|
||||
}
|
||||
if (!$parent || !Vfs::check_access($parent_path,Vfs::WRITABLE, $parent, $this->user))
|
||||
if (!$parent || !$this->check_access($parent_path,Vfs::WRITABLE, $parent))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) permission denied!");
|
||||
@ -753,7 +753,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
'fs_mime' => self::DIR_MIME_TYPE,
|
||||
'fs_created' => self::_pdo_timestamp(time()),
|
||||
'fs_modified' => self::_pdo_timestamp(time()),
|
||||
'fs_creator' => $this->user,
|
||||
'fs_creator' => Vfs::$user,
|
||||
))))
|
||||
{
|
||||
// check if some other process created the directory parallel to us (sqlfs would gives SQL errors later!)
|
||||
@ -793,7 +793,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
|
||||
if (!($parent = Vfs::dirname($path)) ||
|
||||
!($stat = $this->url_stat($path, 0)) || $stat['mime'] != self::DIR_MIME_TYPE ||
|
||||
!Vfs::check_access($parent, Vfs::WRITABLE, $this->url_stat($parent,0), $this->user))
|
||||
!$this->check_access($parent, Vfs::WRITABLE))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
$err_msg = __METHOD__."($url,$options) ".(!$stat ? 'not found!' :
|
||||
@ -1079,7 +1079,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
|
||||
if (!($stat = $this->url_stat($url,0)) || // dir not found
|
||||
!($stat['mode'] & self::MODE_DIR) && $stat['mime'] != self::DIR_MIME_TYPE || // no dir
|
||||
!Vfs::check_access($url,Vfs::EXECUTABLE|Vfs::READABLE, $stat, $this->user)) // no access
|
||||
!$this->check_access($url,Vfs::EXECUTABLE|Vfs::READABLE, $stat)) // no access
|
||||
{
|
||||
self::_remove_password($url);
|
||||
$msg = !($stat['mode'] & self::MODE_DIR) && $stat['mime'] != self::DIR_MIME_TYPE ?
|
||||
@ -1374,7 +1374,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
|
||||
return false; // $link already exists
|
||||
}
|
||||
if (!($dir = Vfs::dirname($link)) ||
|
||||
!Vfs::check_access($dir,Vfs::WRITABLE, $dir_stat=$this->url_stat($dir,0), $this->user))
|
||||
!$this->check_access($dir,Vfs::WRITABLE, $dir_stat=$this->url_stat($dir,0)))
|
||||
{
|
||||
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$target','$link') returning false! (!is_writable('$dir'), dir_stat=".array2string($dir_stat).")");
|
||||
return false; // parent dir does not exist or is not writable
|
||||
@ -1864,7 +1864,7 @@ GROUP BY A.fs_id';
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Vfs::check_access($path,Api\Acl::EDIT, $stat, $this->user))
|
||||
if (!$this->check_access($path,Api\Acl::EDIT, $stat))
|
||||
{
|
||||
return false; // permission denied
|
||||
}
|
||||
|
@ -23,28 +23,15 @@ use EGroupware\Api;
|
||||
*
|
||||
* @link http://www.php.net/manual/en/function.stream-wrapper-register.php
|
||||
*/
|
||||
class StreamWrapper implements StreamWrapperIface
|
||||
class StreamWrapper extends Base implements StreamWrapperIface
|
||||
{
|
||||
use UserContext;
|
||||
use UserContextTrait;
|
||||
|
||||
/**
|
||||
* 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';
|
||||
/**
|
||||
* Should unreadable entries in a not writable directory be hidden, default yes
|
||||
*/
|
||||
const HIDE_UNREADABLES = true;
|
||||
|
||||
/**
|
||||
* mode-bits, which have to be set for links
|
||||
*/
|
||||
const MODE_LINK = 0120000;
|
||||
|
||||
/**
|
||||
* How much should be logged to the apache error-log
|
||||
*
|
||||
@ -317,8 +304,8 @@ class StreamWrapper implements StreamWrapperIface
|
||||
}
|
||||
$this->check_set_context($url, true);
|
||||
|
||||
if (!($this->opened_stream = $context ?
|
||||
fopen($url, $mode, false, $context) : fopen($url, $mode, false)))
|
||||
if (!($this->opened_stream = $this->context ?
|
||||
fopen($url, $mode, false, $this->context) : fopen($url, $mode, false)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -763,7 +750,7 @@ class StreamWrapper implements StreamWrapperIface
|
||||
if (self::LOG_LEVEL > 0) error_log(__METHOD__."( $path,$options) opendir($this->opened_dir_url) failed!");
|
||||
return false;
|
||||
}
|
||||
$this->opened_dir_writable = Vfs::check_access($this->opened_dir_url,Vfs::WRITABLE);
|
||||
$this->opened_dir_writable = $this->check_access($this->opened_dir_url,Vfs::WRITABLE);
|
||||
// check our fstab if we need to add some of the mountpoints
|
||||
$basepath = Vfs::parse_url($path,PHP_URL_PATH);
|
||||
foreach(array_keys(self::$fstab) as $mounted)
|
||||
@ -771,7 +758,7 @@ class StreamWrapper implements StreamWrapperIface
|
||||
if (((Vfs::dirname($mounted) == $basepath || Vfs::dirname($mounted).'/' == $basepath) && $mounted != '/') &&
|
||||
// only return children readable by the user, if dir is not writable
|
||||
(!self::HIDE_UNREADABLES || $this->opened_dir_writable ||
|
||||
Vfs::check_access($mounted,Vfs::READABLE)))
|
||||
$this->check_access($mounted,Vfs::READABLE)))
|
||||
{
|
||||
$this->extra_dirs[] = Vfs::basename($mounted);
|
||||
}
|
||||
@ -934,35 +921,24 @@ clearstatcache(); // testwise, NOT good for performance
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Check if extendes ACL (stored in eGW's ACL table) grants access
|
||||
*
|
||||
* @param string $path path
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = self::READABLE,
|
||||
* 2 = self::WRITABLE, 1 = self::EXECUTABLE
|
||||
* The extended ACL is inherited, so it's valid for all subdirs and the included files!
|
||||
* The used algorithm break on the first match. It could be used, to disallow further access.
|
||||
*
|
||||
* @param string $path path to check
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = read, 2 = write, 1 = executable
|
||||
* @return boolean
|
||||
*/
|
||||
function check_access($path, $check)
|
||||
function check_extended_acl($path, $check)
|
||||
{
|
||||
if (!($stat = $this->url_stat($path, 0)))
|
||||
if (!($url = self::resolve_url($path)))
|
||||
{
|
||||
$ret = false;
|
||||
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path', $check) can NOT resolve path: ".function_backtrace(1));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (($account_lid = Vfs::parse_url($stat['url'], PHP_URL_USER)) &&
|
||||
($account_id = Api\Accounts::getInstance()->name2id($account_lid)))
|
||||
{
|
||||
$user = $account_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$user = $this->user ?: Vfs::$user;
|
||||
}
|
||||
$ret = Vfs::check_access($path, $check, $stat, $user);
|
||||
}
|
||||
error_log(__METHOD__."('$path', $check) user=".Api\Accounts::id2name($user).", effective user=$user=".Api\Accounts::id2name($user).", mode=".decoct($stat['mode'] & 0777).", uid=".($stat['uid']?Api\Accounts::id2name($stat['uid']):0).", uid=".($stat['gid']?Api\Accounts::id2name(-$stat['gid']):0)." returning ".array2string($ret));
|
||||
return $ret;
|
||||
// check backend for extended acls (only if path given)
|
||||
return self::_call_on_backend('check_extended_acl', [$url, $check], true, 0, true); // true = fail silent if backend does not support
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1123,7 +1099,7 @@ clearstatcache(); // testwise, NOT good for performance
|
||||
while($file !== false &&
|
||||
(is_array($this->extra_dirs) && in_array($file,$this->extra_dirs) || // do NOT return extra_dirs twice
|
||||
self::HIDE_UNREADABLES && !$this->opened_dir_writable &&
|
||||
!Vfs::check_access(Vfs::concat($this->opened_dir_url,$file),Vfs::READABLE)));
|
||||
!$this->check_access(Vfs::concat($this->opened_dir_url,$file),Vfs::READABLE)));
|
||||
}
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."( $this->opened_dir ) = '$file'");
|
||||
return $file;
|
||||
|
@ -1,119 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware API: VFS - Trait to store user / account_id in stream context
|
||||
*
|
||||
* @link https://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) 2020 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Vfs;
|
||||
|
||||
use EGroupware\Api\Vfs;
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* Trait to store user / account_id in stream context
|
||||
*
|
||||
* Used by Vfs and SqlFS stream-wrapper.
|
||||
*
|
||||
* @property int $user user / account_id stored in context
|
||||
*/
|
||||
trait UserContext
|
||||
{
|
||||
/**
|
||||
* optional context param when opening the stream, null if no context passed
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* Contructor to set context/user incl. from user in url or passed in context
|
||||
*
|
||||
* @param resource|string|null $url_or_context url with user or context to set
|
||||
*/
|
||||
public function __construct($url_or_context=null)
|
||||
{
|
||||
if (is_resource($url_or_context))
|
||||
{
|
||||
$this->context = $url_or_context;
|
||||
}
|
||||
elseif(is_string($url_or_context))
|
||||
{
|
||||
$this->check_set_context($url_or_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have no user-context, but an url with a user --> set it as context
|
||||
*
|
||||
* @param $url
|
||||
* @param bool $always_set false (default): only set if we have not context or user in context, true: always set
|
||||
*/
|
||||
protected function check_set_context($url, $always_set=false)
|
||||
{
|
||||
if (($always_set || !$this->context || empty(stream_context_get_options($this->context)[Vfs::SCHEME]['user'])) &&
|
||||
$url[0] !== '/' && ($account_lid = Vfs::parse_url($url, PHP_URL_USER)))
|
||||
{
|
||||
$this->user = $account_lid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
switch($name)
|
||||
{
|
||||
case 'user':
|
||||
return $this->context ? stream_context_get_options($this->context)[Vfs::SCHEME]['user'] : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
switch($name)
|
||||
{
|
||||
case 'user':
|
||||
if (!is_int($value) && is_string($value) && !is_numeric($value))
|
||||
{
|
||||
$value = Api\Accounts::getInstance()->name2id($value);
|
||||
}
|
||||
if ($value)
|
||||
{
|
||||
$options = [
|
||||
Vfs::SCHEME => ['user' => (int)$value]
|
||||
];
|
||||
if ($this->context)
|
||||
{
|
||||
stream_context_set_option($this->context, $options);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->context = stream_context_create($options);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->__get($name) !== null;
|
||||
}
|
||||
}
|
207
api/src/Vfs/UserContextTrait.php
Normal file
207
api/src/Vfs/UserContextTrait.php
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware API: VFS - Trait to store user / account_id in stream context
|
||||
*
|
||||
* @link https://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) 2020 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Vfs;
|
||||
|
||||
use EGroupware\Api\Vfs;
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* Trait to store user / account_id in stream context
|
||||
*
|
||||
* Used by Vfs and SqlFS stream-wrapper.
|
||||
*
|
||||
* @property int $user user / account_id stored in context
|
||||
*/
|
||||
trait UserContextTrait
|
||||
{
|
||||
/**
|
||||
* optional context param when opening the stream, null if no context passed
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* Contructor to set context/user incl. from user in url or passed in context
|
||||
*
|
||||
* @param resource|string|null $url_or_context url with user or context to set
|
||||
*/
|
||||
public function __construct($url_or_context=null)
|
||||
{
|
||||
if (is_resource($url_or_context))
|
||||
{
|
||||
$this->context = $url_or_context;
|
||||
}
|
||||
elseif(is_string($url_or_context))
|
||||
{
|
||||
$this->check_set_context($url_or_context, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have no user-context, but an url with a user --> set it as context
|
||||
*
|
||||
* @param $url
|
||||
* @param bool $always_set false (default): only set if we have not context or user in context, true: always set
|
||||
*/
|
||||
protected function check_set_context($url, $always_set=false)
|
||||
{
|
||||
if (($always_set || !$this->context || empty(stream_context_get_options($this->context)[Vfs::SCHEME]['user'])) &&
|
||||
$url[0] !== '/' && ($account_lid = Vfs::parse_url($url, PHP_URL_USER)))
|
||||
{
|
||||
$this->user = $account_lid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 path
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = Vfs::READABLE,
|
||||
* 2 = Vfs::WRITABLE, 1 = Vfs::EXECUTABLE
|
||||
* @param array|boolean $stat =null stat array or false, to not query it again
|
||||
* @return boolean
|
||||
*/
|
||||
function check_access($path, $check, $stat=null)
|
||||
{
|
||||
if (Vfs::$is_root)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// throw exception if stat array is used insead of path, can be removed soon
|
||||
if (is_array($path))
|
||||
{
|
||||
throw new Exception\WrongParameter('path has to be string, use check_access($path,$check,$stat=null)!');
|
||||
}
|
||||
|
||||
// if we have no $stat, delegate whole check to vfs stream-wrapper to correctly deal with shares / effective user-ids
|
||||
if (is_null($stat))
|
||||
{
|
||||
$stat = $this->url_stat($path, 0);
|
||||
}
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)");
|
||||
|
||||
if (!$stat)
|
||||
{
|
||||
//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 = Vfs::parse_url($stat['url'], PHP_URL_SCHEME)) && !(class_exists(Vfs::scheme2class($scheme))))
|
||||
{
|
||||
switch($check)
|
||||
{
|
||||
case Vfs::READABLE:
|
||||
return is_readable($stat['url']);
|
||||
case Vfs::WRITABLE:
|
||||
return is_writable($stat['url']);
|
||||
case Vfs::EXECUTABLE:
|
||||
return is_executable($stat['url']);
|
||||
}
|
||||
}
|
||||
|
||||
// check if other rights grant access
|
||||
if (($stat['mode'] & $check) == $check)
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||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)) == ($check << 6) && $stat['uid'] && $stat['uid'] == $this->user)
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path||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)) == ($check << 3) && $stat['gid'])
|
||||
{
|
||||
if (($memberships = Api\Accounts::getInstance()->memberships($this->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;
|
||||
}
|
||||
}
|
||||
// if we check writable and have a readonly mount --> return false, as backends dont know about r/o url parameter
|
||||
if ($check == Vfs::WRITABLE && Vfs\StreamWrapper::url_is_readonly($stat['url']))
|
||||
{
|
||||
//error_log(__METHOD__."(path=$path, check=writable, ...) failed because mount is readonly");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check extended acls (only if path given)
|
||||
$ret = method_exists($this, 'check_extended_acl') && $path && $this->check_extended_acl($stat['url'] ?? $path, $check);
|
||||
|
||||
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) ".($ret ? 'backend extended acl granted access.' : 'no access!!!'));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
switch($name)
|
||||
{
|
||||
case 'user':
|
||||
return $this->context ? stream_context_get_options($this->context)[Vfs::SCHEME]['user'] : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
switch($name)
|
||||
{
|
||||
case 'user':
|
||||
if (!is_int($value) && is_string($value) && !is_numeric($value))
|
||||
{
|
||||
$value = Api\Accounts::getInstance()->name2id($value);
|
||||
}
|
||||
if ($value)
|
||||
{
|
||||
$options = [
|
||||
Vfs::SCHEME => ['user' => (int)$value]
|
||||
];
|
||||
// do NOT overwrite default context
|
||||
if ($this->context && $this->context !== stream_context_get_default())
|
||||
{
|
||||
stream_context_set_option($this->context, $options);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->context = stream_context_create($options);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->__get($name) !== null;
|
||||
}
|
||||
}
|
@ -306,7 +306,7 @@ class filemanager_hooks
|
||||
{
|
||||
foreach (Api\Hooks::process('filemanager-editor-link', 'collabora') as $app => $link)
|
||||
{
|
||||
if ($link && ($access = \EGroupware\Api\Vfs\Links\StreamWrapper::check_app_rights($app)) &&
|
||||
if ($link && !empty($GLOBALS['egw_info']['user']['apps'][$app]) &&
|
||||
(empty($GLOBALS['egw_info']['user']['preferences']['filemanager']['document_doubleclick_action']) ||
|
||||
$GLOBALS['egw_info']['user']['preferences']['filemanager']['document_doubleclick_action'] == $app))
|
||||
{
|
||||
|
66
vfs-context-links.php
Normal file
66
vfs-context-links.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use EGroupware\Api;
|
||||
use EGroupware\Api\Vfs;
|
||||
|
||||
$GLOBALS['egw_info'] = [
|
||||
'flags' => [
|
||||
'currentapp' => 'login',
|
||||
],
|
||||
];
|
||||
require_once __DIR__.'/header-default.inc.php';
|
||||
|
||||
$egw->session->create($sysop='ralf', '', '', true, false);
|
||||
|
||||
$so = new infolog_so();
|
||||
$so->delete(['info_id' => [1, 2]]);
|
||||
$infolog_sysop = $so->write(['info_id' => 1, 'info_owner' => 5, 'info_subject' => 'Test-InfoLog Ralf', 'info_type' => 'task'], 0, null, true);
|
||||
// anonymous user give not grants, has not shared groups with Ralf or sysop
|
||||
$infolog_anon = $so->write(['info_id' => 2, 'info_owner' => ($anon=Api\Accounts::getInstance()->name2id('anonymous')), 'info_subject' => 'Test-InfoLog Anonymous', 'info_type' => 'task'], 0, null, true);
|
||||
//var_dump($so->read(['info_id' => $infolog_sysop]), $so->read(['info_id' => $infolog_anon]));
|
||||
// anonymous user needs infolog run rights for further tests
|
||||
$acl = new Api\Acl($anon);
|
||||
$acl->add_repository('infolog', 'run', $anon, 1);
|
||||
|
||||
foreach(['links', /* 'stylite.links'*/] as $schema)
|
||||
{
|
||||
Vfs::$is_root = true;
|
||||
Vfs\StreamWrapper::mount("$schema://default/apps", '/apps', false, false);
|
||||
Vfs::$is_root = false;
|
||||
var_dump(Vfs\StreamWrapper::mount());
|
||||
|
||||
$infolog_sysop_dir = "/apps/infolog/$infolog_sysop";
|
||||
$infolog_anon_dir = "/apps/infolog/$infolog_anon";
|
||||
var_dump(file_put_contents("vfs://default$infolog_sysop_dir/test.txt", "Just a test ;)\n"));
|
||||
var_dump("Vfs::proppatch('$infolog_sysop_dir/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])=" . array2string(Vfs::proppatch("$infolog_sysop_dir/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])),
|
||||
"Vfs::propfind('$infolog_sysop_dir/test.txt')=" . json_encode(Vfs::propfind("$infolog_sysop_dir/test.txt"), JSON_UNESCAPED_SLASHES));
|
||||
|
||||
var_dump($f = fopen("vfs://default$infolog_sysop_dir/test.txt", 'r'), fread($f, 100), fclose($f));
|
||||
|
||||
Vfs::$is_root = true;
|
||||
var_dump(file_put_contents("vfs://default$infolog_anon_dir/test.txt", "Just a test ;)\n"));
|
||||
var_dump("Vfs::proppatch('$infolog_anon_dir/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])=" . array2string(Vfs::proppatch("$infolog_anon_dir/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something']])),
|
||||
"Vfs::propfind('$infolog_anon_dir/test.txt')=" . json_encode(Vfs::propfind("$infolog_anon_dir/test.txt"), JSON_UNESCAPED_SLASHES));
|
||||
var_dump(Vfs\StreamWrapper::mount("$schema://anonymous@default$infolog_anon_dir", $share_dir = '/home/ralf/anon-infolog'));
|
||||
Vfs::$is_root = false;
|
||||
|
||||
var_dump(Vfs\StreamWrapper::mount());
|
||||
|
||||
var_dump("Vfs::resolve_url('$share_dir/test.txt')=" . Vfs::resolve_url("$share_dir/test.txt"));
|
||||
var_dump("Vfs::url_stat('$share_dir/test.txt')=" . json_encode(Vfs::stat("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::is_readable('$share_dir/test.txt')=" . json_encode(Vfs::is_readable("$share_dir/test.txt")));
|
||||
var_dump("fopen('vfs://default$share_dir/test.txt', 'r')", $f = fopen("vfs://default$share_dir/test.txt", 'r'), fread($f, 100), fclose($f));
|
||||
var_dump("Vfs::propfind('$share_dir/test.txt')", Vfs::propfind("$share_dir/test.txt"));
|
||||
var_dump("Vfs::proppatch('$share_dir/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])=" . array2string(Vfs::proppatch("$share_dir/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])),
|
||||
"Vfs::propfind('$share_dir/test.txt')=" . json_encode(Vfs::propfind("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));
|
||||
|
||||
var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir")));
|
||||
var_dump("Vfs::mkdir('$share_dir/test-dir')=" . json_encode(Vfs::mkdir("$share_dir/test-dir")));
|
||||
var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir"), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::rmdir('$share_dir/test-dir')=" . json_encode(Vfs::rmdir("$share_dir/test-dir")));
|
||||
var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir")));
|
||||
|
||||
var_dump("Vfs::scandir('$share_dir')=" . json_encode(Vfs::scandir($share_dir), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::remove('$share_dir/test.txt')=" . json_encode(Vfs::remove("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::scandir('$share_dir')=" . json_encode(Vfs::scandir($share_dir), JSON_UNESCAPED_SLASHES));
|
||||
}
|
@ -39,12 +39,12 @@ var_dump("Vfs::propfind('/home/ralf/birgit/test.txt')", Vfs::propfind('/home/ral
|
||||
var_dump("Vfs::proppatch('/home/ralf/birgit/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])=".array2string(Vfs::proppatch('/home/ralf/birgit/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])),
|
||||
"Vfs::propfind('/home/ralf/birgit/test.txt')=".json_encode(Vfs::propfind('/home/ralf/birgit/test.txt'), JSON_UNESCAPED_SLASHES));
|
||||
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test.txt')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir')));
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test-dir')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir')));
|
||||
var_dump("Vfs::mkdir('/home/ralf/birgit/test-dir')=".json_encode(Vfs::mkdir('/home/ralf/birgit/test-dir')));
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test.txt')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir'), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test-dir')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir'), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::rmdir('/home/ralf/birgit/test-dir')=".json_encode(Vfs::rmdir('/home/ralf/birgit/test-dir')));
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test.txt')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir')));
|
||||
var_dump("Vfs::url_stat('/home/ralf/birgit/test-dir')=".json_encode(Vfs::stat('/home/ralf/birgit/test-dir')));
|
||||
|
||||
var_dump("Vfs::scandir('/home/ralf/birgit')=".json_encode(Vfs::scandir('/home/ralf/birgit'), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::remove('/home/ralf/birgit/test.txt')=".json_encode(Vfs::remove('/home/ralf/birgit/test.txt')));
|
||||
var_dump("Vfs::remove('/home/ralf/birgit/test.txt')=".json_encode(Vfs::remove('/home/ralf/birgit/test.txt'), JSON_UNESCAPED_SLASHES));
|
||||
var_dump("Vfs::scandir('/home/ralf/birgit')=".json_encode(Vfs::scandir('/home/ralf/birgit'), JSON_UNESCAPED_SLASHES));
|
||||
|
Loading…
Reference in New Issue
Block a user