Merge branch 'non-static-stream-wrapper'

This commit is contained in:
Ralf Becker 2016-07-21 10:06:19 +02:00
commit 4f7fa8f018
11 changed files with 781 additions and 522 deletions

View File

@ -55,21 +55,22 @@ before_script:
"hhvm") "hhvm")
;; ;;
esac esac
- php -m #- php -m
# - mysql -e 'create database egroupware'
- rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - rm -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
#- composer update --prefer-source $LOWEST_DEPS #- composer update --prefer-source $LOWEST_DEPS
- mr --trust-all --stats up - mr --trust-all --stats up
- (cd activesync; mr --trust-all --stats up)
# installing phpunit in version suitable for used PHP version # installing phpunit in version suitable for used PHP version
- composer update phpunit/phpunit - composer update phpunit/phpunit
# install egroupware # create data directory
- sudo mkdir /var/lib/egroupware - sudo mkdir /var/lib/egroupware
- sudo chown travis /var/lib/egroupware - sudo chown travis /var/lib/egroupware
script: script:
# install egroupware
- php doc/rpm-build/post_install.php - php doc/rpm-build/post_install.php
--source_dir `pwd` --start_db '' --autostart_db '' || true --source_dir `pwd` --start_db '' --autostart_db '' --start_webserver '' --webserver_user ''
- mysql -uroot -e 'show tables' egroupware || true #- mysql -uroot -e 'show tables' egroupware
# Ubuntu has problems with #!/usr/bin/env php -dapc.enable=1, it stalls forever # Ubuntu has problems with #!/usr/bin/env php -dapc.enable=1, it stalls forever
- php -dapc.enable_cli=1 doc/test-cli.php - php -dapc.enable_cli=1 doc/test-cli.php
- ./doc/php_syntax_check.sh - ./doc/php_syntax_check.sh

View File

@ -1300,7 +1300,7 @@ class Link extends Link\Storage
if (!is_array($fileinfo)) if (!is_array($fileinfo))
{ {
$url = Vfs\Sqlfs\StreamWrapper::id2path($fileinfo); $url = Vfs\Sqlfs\StreamWrapper::id2path($fileinfo);
if (!($fileinfo = Vfs::url_stat($url,STREAM_URL_STAT_QUIET))) if (!($fileinfo = Vfs::stat($url,STREAM_URL_STAT_QUIET)))
{ {
return false; return false;
} }

View File

@ -67,9 +67,17 @@ use HTTP_WebDAV_Server;
* Vfs::parse_url($url, $component=-1), Vfs::dirname($url) and Vfs::basename($url) work * 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! * on urls containing utf-8 characters, which get NOT urlencoded in our VFS!
*/ */
class Vfs extends Vfs\StreamWrapper class Vfs
{ {
const PREFIX = 'vfs://default'; 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 * Readable bit, for dirs traversable
*/ */
@ -86,6 +94,14 @@ class Vfs extends Vfs\StreamWrapper
* Name of the lock table * Name of the lock table
*/ */
const LOCK_TABLE = 'egw_locks'; const LOCK_TABLE = 'egw_locks';
/**
* How much should be logged to the apache error-log
*
* 0 = Nothing
* 1 = only errors
* 2 = all function calls and errors (contains passwords too!)
*/
const LOG_LEVEL = 1;
/** /**
* Current user has root rights, no access checks performed! * Current user has root rights, no access checks performed!
* *
@ -238,11 +254,12 @@ class Vfs extends Vfs\StreamWrapper
*/ */
static function stat($path,$try_create_home=false) static function stat($path,$try_create_home=false)
{ {
if ($path[0] != '/') if ($path[0] != '/' && strpos($path, self::PREFIX.'/') !== 0)
{ {
throw new Exception\AssertionFailed("File '$path' is not an absolute path!"); throw new Exception\AssertionFailed("File '$path' is not an absolute path!");
} }
if (($stat = self::url_stat($path,0,$try_create_home))) $vfs = new Vfs\StreamWrapper();
if (($stat = $vfs->url_stat($path,0,$try_create_home)))
{ {
$stat = array_slice($stat,13); // remove numerical indices 0-12 $stat = array_slice($stat,13); // remove numerical indices 0-12
} }
@ -262,7 +279,8 @@ class Vfs extends Vfs\StreamWrapper
{ {
throw new Exception\AssertionFailed("File '$path' is not an absolute path!"); throw new Exception\AssertionFailed("File '$path' is not an absolute path!");
} }
if (($stat = self::url_stat($path,STREAM_URL_STAT_LINK,$try_create_home))) $vfs = new Vfs\StreamWrapper();
if (($stat = $vfs->url_stat($path,STREAM_URL_STAT_LINK,$try_create_home)))
{ {
$stat = array_slice($stat,13); // remove numerical indices 0-12 $stat = array_slice($stat,13); // remove numerical indices 0-12
} }
@ -317,69 +335,7 @@ class Vfs extends Vfs\StreamWrapper
*/ */
static function mount($url=null,$path=null,$check_url=null,$persitent_mount=true,$clear_fstab=false) static function mount($url=null,$path=null,$check_url=null,$persitent_mount=true,$clear_fstab=false)
{ {
if (is_null($check_url)) $check_url = strpos($url,'$') === false; return Vfs\StreamWrapper::mount($url, $path, $check_url, $persitent_mount, $clear_fstab);
if (!isset($GLOBALS['egw_info']['server']['vfs_fstab'])) // happens eg. in setup
{
$api_config = Config::read('phpgwapi');
if (isset($api_config['vfs_fstab']) && is_array($api_config['vfs_fstab']))
{
self::$fstab = $api_config['vfs_fstab'];
}
else
{
self::$fstab = array(
'/' => 'sqlfs://$host/',
'/apps' => 'links://$host/apps',
);
}
unset($api_config);
}
if (is_null($url) || is_null($path))
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') returns '.array2string(self::$fstab));
return self::$fstab;
}
if (!self::$is_root)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') permission denied, you are NOT root!');
return false; // only root can mount
}
if ($clear_fstab)
{
self::$fstab = array();
}
if (isset(self::$fstab[$path]) && self::$fstab[$path] === $url)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') already mounted.');
return true; // already mounted
}
self::load_wrapper(self::parse_url($url,PHP_URL_SCHEME));
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
}
self::$fstab[$path] = $url;
uksort(self::$fstab, function($a, $b)
{
return strlen($a) - strlen($b);
});
if ($persitent_mount)
{
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;
} }
/** /**
@ -389,27 +345,18 @@ class Vfs extends Vfs\StreamWrapper
*/ */
static function umount($path) static function umount($path)
{ {
if (!self::$is_root) return Vfs\StreamWrapper::umount($path);
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($path).','.array2string($path).') permission denied, you are NOT root!');
return false; // only root can mount
} }
if (!isset(self::$fstab[$path]) && ($path = array_search($path,self::$fstab)) === false)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($path).') NOT mounted!');
return false; // $path not mounted
}
unset(self::$fstab[$path]);
Config::save_value('vfs_fstab',self::$fstab,'phpgwapi'); /**
$GLOBALS['egw_info']['server']['vfs_fstab'] = self::$fstab; * Returns mount url of a full url returned by resolve_url
// invalidate session cache *
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited * @param string $fullurl full url returned by resolve_url
* @return string|NULL mount url or null if not found
*/
static function mount_url($fullurl)
{ {
$GLOBALS['egw']->invalidate_session_cache(); return Vfs\StreamWrapper::mount_url($fullurl);
}
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($path).') returns true (successful unmount).');
return true;
} }
/** /**
@ -679,11 +626,14 @@ class Vfs extends Vfs\StreamWrapper
if ($options['url']) if ($options['url'])
{ {
$stat = @lstat($path); if (($stat = @lstat($path)))
{
$stat = array_slice($stat,13); // remove numerical indices 0-12
}
} }
else else
{ {
$stat = self::url_stat($path,STREAM_URL_STAT_LINK); $stat = self::stat($path, STREAM_URL_STAT_LINK);
} }
if (!$stat) if (!$stat)
{ {
@ -694,7 +644,6 @@ class Vfs extends Vfs\StreamWrapper
{ {
return; // wrong type return; // wrong type
} }
$stat = array_slice($stat,13); // remove numerical indices 0-12
$stat['path'] = self::parse_url($path,PHP_URL_PATH); $stat['path'] = self::parse_url($path,PHP_URL_PATH);
$stat['name'] = $options['remove'] > 0 ? implode('/',array_slice(explode('/',$stat['path']),$options['remove'])) : self::basename($path); $stat['name'] = $options['remove'] > 0 ? implode('/',array_slice(explode('/',$stat['path']),$options['remove'])) : self::basename($path);
@ -802,11 +751,12 @@ class Vfs extends Vfs\StreamWrapper
{ {
$url = self::PREFIX . $url; $url = self::PREFIX . $url;
} }
$vfs = new Vfs\StreamWrapper();
if (is_dir($url) && !is_link($url)) if (is_dir($url) && !is_link($url))
{ {
return self::rmdir($url,0); return $vfs->rmdir($url,0);
} }
return self::unlink($url); return $vfs->unlink($url);
} }
/** /**
@ -847,7 +797,8 @@ class Vfs extends Vfs\StreamWrapper
{ {
self::clearstatcache($path); self::clearstatcache($path);
$path_user_stat[$path][$user] = self::url_stat($path, 0); $vfs = new Vfs\StreamWrapper();
$path_user_stat[$path][$user] = $vfs->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! self::clearstatcache($path); // we need to clear the stat-cache after the call too, as the next call might be the regular user again!
} }
@ -889,7 +840,8 @@ class Vfs extends Vfs\StreamWrapper
// query stat array, if not given // query stat array, if not given
if (is_null($stat)) if (is_null($stat))
{ {
$stat = self::url_stat($path,0); if (!isset($vfs)) $vfs = new Vfs\StreamWrapper();
$stat = $vfs->url_stat($path,0);
} }
//error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)"); //error_log(__METHOD__."(path=$path||stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)");
@ -1307,6 +1259,59 @@ class Vfs extends Vfs\StreamWrapper
return array_pop($parts); return array_pop($parts);
} }
/**
* Utf-8 save version of parse_url
*
* Does caching withing request, to not have to parse urls over and over again.
*
* @param string $url
* @param int $component =-1 PHP_URL_* constants
* @return array|string|boolean on success array or string, if $component given, or false on failure
*/
static function parse_url($url, $component=-1)
{
static $component2str = array(
PHP_URL_SCHEME => 'scheme',
PHP_URL_HOST => 'host',
PHP_URL_PORT => 'port',
PHP_URL_USER => 'user',
PHP_URL_PASS => 'pass',
PHP_URL_PATH => 'path',
PHP_URL_QUERY => 'query',
PHP_URL_FRAGMENT => 'fragment',
);
static $cache = array(); // some caching
$result =& $cache[$url];
if (!isset($result))
{
// Build arrays of values we need to decode before parsing
static $entities = array('%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%24', '%2C', '%2F', '%3F', '%23', '%5B', '%5D');
static $replacements = array('!', '*', "'", "(", ")", ";", ":", "@", "&", "=", "$", ",", "/", "?", "#", "[", "]");
static $str_replace = null;
if (!isset($str_replace)) $str_replace = function_exists('mb_str_replace') ? 'mb_str_replace' : 'str_replace';
// Create encoded URL with special URL characters decoded so it can be parsed
// All other characters will be encoded
$encodedURL = $str_replace($entities, $replacements, urlencode($url));
// Parse the encoded URL
$result = $encodedParts = parse_url($encodedURL);
// Now, decode each value of the resulting array
if ($encodedParts)
{
$result = array();
foreach ($encodedParts as $key => $value)
{
$result[$key] = urldecode($str_replace($replacements, $entities, $value));
}
}
}
return $component >= 0 ? $result[$component2str[$component]] : $result;
}
/** /**
* Get the directory / parent of a given path or url(!), return false for '/'! * Get the directory / parent of a given path or url(!), return false for '/'!
* *
@ -1346,8 +1351,11 @@ class Vfs extends Vfs\StreamWrapper
*/ */
static function has_owner_rights($path,array $stat=null) static function has_owner_rights($path,array $stat=null)
{ {
if (!$stat) $stat = self::url_stat($path,0); if (!$stat)
{
$vfs = new Vfs\StreamWrapper();
$stat = $vfs->url_stat($path,0);
}
return $stat['uid'] == self::$user || // user is the owner return $stat['uid'] == self::$user || // user is the owner
self::$is_root || // class runs with root rights self::$is_root || // class runs with root rights
!$stat['uid'] && $stat['gid'] && self::$is_admin; // group directory and user is an eGW admin !$stat['uid'] && $stat['gid'] && self::$is_admin; // group directory and user is an eGW admin
@ -2020,11 +2028,12 @@ class Vfs extends Vfs\StreamWrapper
{ {
if (self::is_dir($dst)) if (self::is_dir($dst))
{ {
$vfs = new Vfs\StreamWrapper();
foreach($src as $file) foreach($src as $file)
{ {
$target = self::concat($dst, self::basename($file)); $target = self::concat($dst, self::basename($file));
if ($file != $target && self::rename($file, $target)) if ($file != $target && $vfs->rename($file, $target))
{ {
$moved[] = $file; $moved[] = $file;
} }
@ -2145,6 +2154,325 @@ class Vfs extends Vfs\StreamWrapper
//error_log(__METHOD__."($file1, $file2) returning ".array2string($read1 === $read2)." (content differs)"); //error_log(__METHOD__."($file1, $file2) returning ".array2string($read1 === $read2)." (content differs)");
return $read1 === $read2; return $read1 === $read2;
} }
/**
* Resolve the given path according to our fstab AND symlinks
*
* @param string $_path
* @param boolean $file_exists =true true if file needs to exists, false if not
* @param boolean $resolve_last_symlink =true
* @param array|boolean &$stat=null on return: stat of existing file or false for non-existing files
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
*/
static function resolve_url_symlinks($_path,$file_exists=true,$resolve_last_symlink=true,&$stat=null)
{
$vfs = new Vfs\StreamWrapper();
return $vfs->resolve_url_symlinks($_path, $file_exists, $resolve_last_symlink, $stat);
}
/**
* Resolve the given path according to our fstab
*
* @param string $_path
* @param boolean $do_symlink =true is a direct match allowed, default yes (must be false for a lstat or readlink!)
* @param boolean $use_symlinkcache =true
* @param boolean $replace_user_pass_host =true replace $user,$pass,$host in url, default true, if false result is not cached
* @param boolean $fix_url_query =false true append relativ path to url query parameter, default not
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
*/
static function resolve_url($_path,$do_symlink=true,$use_symlinkcache=true,$replace_user_pass_host=true,$fix_url_query=false)
{
Vfs\StreamWrapper::resolve_url($_path, $do_symlink, $use_symlinkcache, $replace_user_pass_host, $fix_url_query);
}
/**
* This method is called in response to mkdir() calls on URL paths associated with the wrapper.
*
* It should attempt to create the directory specified by path.
* In order for the appropriate error message to be returned, do not define this method if your wrapper does not support creating directories.
*
* @param string $path
* @param int $mode
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
* @return boolean TRUE on success or FALSE on failure
*/
static function mkdir ($path, $mode=0750, $options=0)
{
return $path[0] == '/' && mkdir(self::PREFIX.$path, $mode, $options);
}
/**
* This method is called in response to rmdir() calls on URL paths associated with the wrapper.
*
* It should attempt to remove the directory specified by path.
* In order for the appropriate error message to be returned, do not define this method if your wrapper does not support removing directories.
*
* @param string $path
* @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure.
*/
static function rmdir ( $path, $options=0 )
{
return $path[0] == '/' && rmdir(self::PREFIX.$path, $options);
}
/**
* This method is called in response to unlink() calls on URL paths associated with the wrapper.
*
* It should attempt to delete the item specified by path.
* In order for the appropriate error message to be returned, do not define this method if your wrapper does not support unlinking!
*
* @param string $path
* @return boolean TRUE on success or FALSE on failure
*/
static function unlink ( $path )
{
return $path[0] == '/' && unlink(self::PREFIX.$path);
}
/**
* 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
* @return mixed return value of backend or false if function does not exist on backend
*/
static protected function _call_on_backend($name,$params,$fail_silent=false,$path_param_key=0)
{
$pathes = $params[$path_param_key];
$scheme2urls = array();
foreach(is_array($pathes) ? $pathes : array($pathes) as $path)
{
if (!($url = self::resolve_url_symlinks($path,false,false)))
{
return false;
}
$k=(string)self::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 = self::scheme2class($scheme)) || !method_exists($class,$name))
{
if (!$fail_silent) trigger_error("Can't $name for scheme $scheme!\n",E_USER_WARNING);
return false;
}
if (!is_array($pathes))
{
$params[$path_param_key] = $url;
return call_user_func_array(array($class,$name),$params);
}
$params[$path_param_key] = $urls;
if (!is_array($r = call_user_func_array(array($class,$name),$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=self::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;
}
/**
* touch just running on VFS path
*
* @param string $path
* @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
* @return boolean true on success, false otherwise
*/
static function touch($path,$time=null,$atime=null)
{
return $path[0] == '/' && touch(self::PREFIX.$path, $time, $atime);
}
/**
* chmod just running on VFS path
*
* Requires owner or root rights!
*
* @param string $path
* @param string $mode mode string see Vfs::mode2int
* @return boolean true on success, false otherwise
*/
static function chmod($path,$mode)
{
return $path[0] == '/' && chmod(self::PREFIX.$path, $mode);
}
/**
* chmod just running on VFS path
*
* Requires root rights!
*
* @param string $path
* @param int|string $owner numeric user id or account-name
* @return boolean true on success, false otherwise
*/
static function chown($path,$owner)
{
return $path[0] == '/' && chown(self::PREFIX.$path, $owner);
}
/**
* chgrp just running on VFS path
*
* Requires owner or root rights!
*
* @param string $path
* @param int|string $group numeric group id or group-name
* @return boolean true on success, false otherwise
*/
static function chgrp($path,$group)
{
return $path[0] == '/' && chown(self::PREFIX.$path, $group);
}
/**
* Returns the target of a symbolic link
*
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* @param string $path
* @return string|boolean link-data or false if no link
*/
static function readlink($path)
{
$ret = self::_call_on_backend('readlink',array($path),true); // true = fail silent, if backend does not support readlink
//error_log(__METHOD__."('$path') returning ".array2string($ret).' '.function_backtrace());
return $ret;
}
/**
* Creates a symbolic link
*
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* @param string $target target of the link
* @param string $link path of the link to create
* @return boolean true on success, false on error
*/
static function symlink($target,$link)
{
if (($ret = self::_call_on_backend('symlink',array($target,$link),false,1))) // 1=path is in $link!
{
self::symlinkCache_remove($link);
}
return $ret;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* The methods use the following ways to get the mime type (in that order)
* - directories (is_dir()) --> self::DIR_MIME_TYPE
* - stream implemented by class defining the STAT_RETURN_MIME_TYPE constant --> use mime-type returned by url_stat
* - for regular filesystem use mime_content_type function if available
* - use eGW's mime-magic class
*
* @param string $path
* @param boolean $recheck =false true = do a new check, false = rely on stored mime type (if existing)
* @return string mime-type (self::DIR_MIME_TYPE for directories)
*/
static function mime_content_type($path,$recheck=false)
{
if (!($url = self::resolve_url_symlinks($path)))
{
return false;
}
if (($scheme = self::parse_url($url,PHP_URL_SCHEME)) && !$recheck)
{
// check it it's an eGW stream wrapper returning mime-type via url_stat
// we need to first check if the constant is defined, as we get a fatal error in php5.3 otherwise
if (class_exists($class = self::scheme2class($scheme)) &&
defined($class.'::STAT_RETURN_MIME_TYPE') &&
($mime_attr = constant($class.'::STAT_RETURN_MIME_TYPE')))
{
$inst = new $class;
$stat = $inst->url_stat(self::parse_url($url,PHP_URL_PATH),0);
if ($stat && $stat[$mime_attr])
{
$mime = $stat[$mime_attr];
}
}
}
if (!$mime && is_dir($url))
{
$mime = self::DIR_MIME_TYPE;
}
// if we operate on the regular filesystem and the mime_content_type function is available --> use it
if (!$mime && !$scheme && function_exists('mime_content_type'))
{
$mime = mime_content_type($path);
}
// using EGw's own mime magic (currently only checking the extension!)
if (!$mime)
{
$mime = MimeMagic::filename2mime(self::parse_url($url,PHP_URL_PATH));
}
//error_log(__METHOD__."($path,$recheck) mime=$mime");
return $mime;
}
/**
* Get the class-name for a scheme
*
* A scheme is not allowed to contain an underscore, but allows a dot and a class names only allow or need underscores, but no dots
* --> we replace dots in scheme with underscored to get the class-name
*
* @param string $scheme eg. vfs
* @return string
*/
static function scheme2class($scheme)
{
return Vfs\StreamWrapper::scheme2class($scheme);
}
/**
* Clears our internal stat and symlink cache
*
* Normaly not necessary, as it is automatically cleared/updated, UNLESS Vfs::$user changes!
*
* We have to clear the symlink cache before AND after calling the backend,
* because auf traversal rights may be different when Vfs::$user changes!
*
* @param string $path ='/' path of backend, whos cache to clear
*/
static function clearstatcache($path='/')
{
//error_log(__METHOD__."('$path')");
Vfs\StreamWrapper::clearstatcache($path);
self::_call_on_backend('clearstatcache', array($path), true, 0);
Vfs\StreamWrapper::clearstatcache($path);
}
} }
Vfs::init_static(); Vfs::init_static();

View File

@ -66,7 +66,7 @@ class File extends DAV\FS\File
*/ */
function getETag() function getETag()
{ {
if (($stat = Vfs::url_stat($this->vfs_path, STREAM_URL_STAT_QUIET))) if (($stat = Vfs::stat($this->vfs_path, STREAM_URL_STAT_QUIET)))
{ {
return '"'.$stat['ino'].':'.$stat['mtime'].':'.$stat['size'].'"'; return '"'.$stat['ino'].':'.$stat['mtime'].':'.$stat['size'].'"';
} }

View File

@ -306,7 +306,7 @@ class StreamWrapper implements Vfs\StreamWrapperIface
* @param string $url * @param string $url
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function unlink ( $url ) function unlink ( $url )
{ {
$path = Vfs::decodePath(Vfs::parse_url($url,PHP_URL_PATH)); $path = Vfs::decodePath(Vfs::parse_url($url,PHP_URL_PATH));
@ -331,7 +331,7 @@ class StreamWrapper implements Vfs\StreamWrapperIface
* @param string $url_to * @param string $url_to
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function rename ( $url_from, $url_to ) function rename ( $url_from, $url_to )
{ {
$from = Vfs::parse_url($url_from); $from = Vfs::parse_url($url_from);
$to = Vfs::parse_url($url_to); $to = Vfs::parse_url($url_to);
@ -381,7 +381,7 @@ class StreamWrapper implements Vfs\StreamWrapperIface
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function mkdir ( $url, $mode, $options ) function mkdir ( $url, $mode, $options )
{ {
unset($mode); // not used, but required by interface unset($mode); // not used, but required by interface
@ -415,7 +415,7 @@ class StreamWrapper implements Vfs\StreamWrapperIface
* @param int $options Possible values include STREAM_REPORT_ERRORS. * @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure. * @return boolean TRUE on success or FALSE on failure.
*/ */
static function rmdir ( $url, $options ) function rmdir ( $url, $options )
{ {
unset($options); // not used, but required by interface unset($options); // not used, but required by interface
@ -432,16 +432,27 @@ class StreamWrapper implements Vfs\StreamWrapperIface
} }
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* We only implement touch, as other functionality would require webserver to run as root.
* *
* @param string $url * @param string $url
* @param int $time =null modification time (unix timestamp), default null = current time * @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs! * @param array|int|string $value
* @return boolean true on success, false otherwise * - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/ */
static function touch($url,$time=null,$atime=null) function stream_metadata($url, $option, $value)
{ {
$path = Vfs::decodePath(Vfs::parse_url($url,PHP_URL_PATH)); if ($option != STREAM_META_TOUCH)
{
return false; // not implemented / supported
}
$path = Vfs::decodePath(Vfs::parse_url($url, PHP_URL_PATH));
$parent = dirname($path); $parent = dirname($path);
// check access rights (in real filesystem AND by mount perms) // check access rights (in real filesystem AND by mount perms)
@ -450,55 +461,9 @@ class StreamWrapper implements Vfs\StreamWrapperIface
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!"); if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
return false; return false;
} }
return touch($path,$time,$atime);
}
/** array_unshift($value, $path);
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static return call_user_func_array('touch', $value);
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param string $mode mode string see Vfs::mode2int
* @return boolean true on success, false otherwise
*/
static function chmod($path,$mode)
{
unset($path, $mode); // not used, but required by interface
return false;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param int $owner numeric user id
* @return boolean true on success, false otherwise
*/
static function chown($path,$owner)
{
unset($path, $owner); // not used, but required by interface
return false;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Not supported, as it would require root rights!
*
* @param string $path
* @param int $group numeric group id
* @return boolean true on success, false otherwise
*/
static function chgrp($path,$group)
{
unset($path, $group); // not used, but required by interface
return false;
} }
/** /**
@ -552,7 +517,7 @@ class StreamWrapper implements Vfs\StreamWrapperIface
* stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!
* @return array * @return array
*/ */
static function url_stat ( $url, $flags ) function url_stat ( $url, $flags )
{ {
$parts = Vfs::parse_url($url); $parts = Vfs::parse_url($url);
$path = Vfs::decodePath($parts['path']); $path = Vfs::decodePath($parts['path']);

View File

@ -137,7 +137,7 @@ class StreamWrapper extends LinksParent
* stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!
* @return array * @return array
*/ */
static function url_stat ( $url, $flags ) function url_stat ( $url, $flags )
{ {
$eacl_check=self::check_extended_acl($url,Vfs::READABLE); $eacl_check=self::check_extended_acl($url,Vfs::READABLE);
@ -232,7 +232,7 @@ class StreamWrapper extends LinksParent
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE, we allways use recursive! * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE, we allways use recursive!
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function mkdir($path,$mode,$options) function mkdir($path,$mode,$options)
{ {
unset($mode); // not used, but required by function signature unset($mode); // not used, but required by function signature
@ -329,6 +329,29 @@ class StreamWrapper extends LinksParent
return parent::dir_opendir($url, $options); return parent::dir_opendir($url, $options);
} }
/**
* Reimplemented to create an entry directory on the fly AND delete our stat cache!
*
* @param string $url
* @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
*/
protected function touch($url,$time=null,$atime=null)
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$time,$atime)");
if (!($stat = self::url_stat($url,STREAM_URL_STAT_QUIET)))
{
// file does not exist --> create an empty one
if (!($f = fopen(self::SCHEME.'://default'.Vfs::parse_url($url,PHP_URL_PATH),'w')) || !fclose($f))
{
return false;
}
}
return is_null($time) ? true : parent::touch($url,$time,$atime);
}
/** /**
* Register this stream-wrapper * Register this stream-wrapper
*/ */

View File

@ -543,7 +543,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param string $url * @param string $url
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function unlink ( $url, $parent_stat=null ) function unlink ( $url, $parent_stat=null )
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");
@ -553,10 +553,10 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
if (!isset($parent_stat)) if (!isset($parent_stat))
{ {
$parent_stat = !($dir = Vfs::dirname($path)) ? false : $parent_stat = !($dir = Vfs::dirname($path)) ? false :
static::url_stat($dir, STREAM_URL_STAT_LINK); $this->url_stat($dir, STREAM_URL_STAT_LINK);
} }
if (!$parent_stat || !($stat = self::url_stat($path,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)) !$dir || !Vfs::check_access($dir, Vfs::WRITABLE, $parent_stat))
{ {
self::_remove_password($url); self::_remove_password($url);
@ -599,7 +599,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param string $url_to * @param string $url_to
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function rename ( $url_from, $url_to) function rename ( $url_from, $url_to)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url_from,$url_to)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url_from,$url_to)");
@ -693,13 +693,13 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function mkdir ( $url, $mode, $options ) function mkdir ( $url, $mode, $options )
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)");
if (self::LOG_LEVEL > 1) error_log(__METHOD__." called from:".function_backtrace()); if (self::LOG_LEVEL > 1) error_log(__METHOD__." called from:".function_backtrace());
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (self::url_stat($path,STREAM_URL_STAT_QUIET)) if ($this->url_stat($path,STREAM_URL_STAT_QUIET))
{ {
self::_remove_password($url); self::_remove_password($url);
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) already exist!"); if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) already exist!");
@ -720,7 +720,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
return false; return false;
} }
if (($query = Vfs::parse_url($url,PHP_URL_QUERY))) $parent_path .= '?'.$query; if (($query = Vfs::parse_url($url,PHP_URL_QUERY))) $parent_path .= '?'.$query;
$parent = self::url_stat($parent_path,STREAM_URL_STAT_QUIET); $parent = $this->url_stat($parent_path,STREAM_URL_STAT_QUIET);
// check if we should also create all non-existing path components and our parent does not exist, // check if we should also create all non-existing path components and our parent does not exist,
// if yes call ourself recursive with the parent directory // if yes call ourself recursive with the parent directory
@ -731,7 +731,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
{ {
return false; return false;
} }
$parent = self::url_stat($parent_path,0); $parent = $this->url_stat($parent_path,0);
} }
if (!$parent || !Vfs::check_access($parent_path,Vfs::WRITABLE,$parent)) if (!$parent || !Vfs::check_access($parent_path,Vfs::WRITABLE,$parent))
{ {
@ -788,14 +788,14 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $options Possible values include STREAM_REPORT_ERRORS. * @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure. * @return boolean TRUE on success or FALSE on failure.
*/ */
static function rmdir ( $url, $options ) function rmdir ( $url, $options )
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($parent = Vfs::dirname($path)) || if (!($parent = Vfs::dirname($path)) ||
!($stat = self::url_stat($path, 0)) || $stat['mime'] != self::DIR_MIME_TYPE || !($stat = $this->url_stat($path, 0)) || $stat['mime'] != self::DIR_MIME_TYPE ||
!Vfs::check_access($parent, Vfs::WRITABLE, static::url_stat($parent,0))) !Vfs::check_access($parent, Vfs::WRITABLE, static::url_stat($parent,0)))
{ {
self::_remove_password($url); self::_remove_password($url);
@ -835,6 +835,49 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
return $ret; return $ret;
} }
/**
* StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* We use protected helper methods touch, chmod, chown and chgrp to implement the functionality.
*
* @param string $path
* @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param array|int|string $value
* - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/
function stream_metadata($path, $option, $value)
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path, $option, ".array2string($value).")");
switch($option)
{
case STREAM_META_TOUCH:
return $this->touch($path, $value[0]); // atime is not supported
case STREAM_META_ACCESS:
return $this->chmod($path, $value);
case STREAM_META_OWNER_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'u')) === false)
return false;
// fall through
case STREAM_META_OWNER:
return $this->chown($path, $value);
case STREAM_META_GROUP_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'g')) === false)
return false;
// fall through
case STREAM_META_GROUP:
return $this->chgrp($path, $value);
}
return false;
}
/** /**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static * This is not (yet) a stream-wrapper function, but it's necessary and can be used static
* *
@ -842,14 +885,15 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $time =null modification time (unix timestamp), default null = current time * @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs! * @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
*/ */
static function touch($url,$time=null,$atime=null) protected function touch($url,$time=null,$atime=null)
{ {
unset($atime); // not used unset($atime); // not used
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $time)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $time)");
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($stat = self::url_stat($path,STREAM_URL_STAT_QUIET))) $vfs = new self();
if (!($stat = $vfs->url_stat($path,STREAM_URL_STAT_QUIET)))
{ {
// file does not exist --> create an empty one // file does not exist --> create an empty one
if (!($f = fopen(self::SCHEME.'://default'.$path,'w')) || !fclose($f)) if (!($f = fopen(self::SCHEME.'://default'.$path,'w')) || !fclose($f))
@ -860,7 +904,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
{ {
return true; // new (empty) file created with current mod time return true; // new (empty) file created with current mod time
} }
$stat = self::url_stat($path,0); $stat = $vfs->url_stat($path,0);
} }
unset(self::$stat_cache[$path]); unset(self::$stat_cache[$path]);
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_modified=:fs_modified,fs_modifier=:fs_modifier WHERE fs_id=:fs_id'); $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_modified=:fs_modified,fs_modifier=:fs_modifier WHERE fs_id=:fs_id');
@ -879,13 +923,14 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $owner * @param int $owner
* @return boolean * @return boolean
*/ */
static function chown($url,$owner) protected function chown($url,$owner)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($stat = self::url_stat($path,0))) $vfs = new self();
if (!($stat = $vfs->url_stat($path,0)))
{ {
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!"); if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!");
trigger_error("No such file or directory $url !",E_USER_WARNING); trigger_error("No such file or directory $url !",E_USER_WARNING);
@ -923,13 +968,14 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $owner * @param int $owner
* @return boolean * @return boolean
*/ */
static function chgrp($url,$owner) protected function chgrp($url,$owner)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($stat = self::url_stat($path,0))) $vfs = new self();
if (!($stat = $vfs->url_stat($path,0)))
{ {
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!"); if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!");
trigger_error("No such file or directory $url !",E_USER_WARNING); trigger_error("No such file or directory $url !",E_USER_WARNING);
@ -968,13 +1014,14 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param int $mode * @param int $mode
* @return boolean * @return boolean
*/ */
static function chmod($url,$mode) protected function chmod($url,$mode)
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $mode)"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url, $mode)");
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($stat = self::url_stat($path,0))) $vfs = new self();
if (!($stat = $vfs->url_stat($path,0)))
{ {
if (self::LOG_LEVEL) error_log(__METHOD__."($url, $mode) no such file or directory!"); if (self::LOG_LEVEL) error_log(__METHOD__."($url, $mode) no such file or directory!");
trigger_error("No such file or directory $url !",E_USER_WARNING); trigger_error("No such file or directory $url !",E_USER_WARNING);
@ -1018,7 +1065,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
$path = Vfs::parse_url($url,PHP_URL_PATH); $path = Vfs::parse_url($url,PHP_URL_PATH);
if (!($stat = self::url_stat($url,0)) || // dir not found if (!($stat = $this->url_stat($url,0)) || // dir not found
$stat['mime'] != self::DIR_MIME_TYPE || // no dir $stat['mime'] != self::DIR_MIME_TYPE || // no dir
!Vfs::check_access($url,Vfs::EXECUTABLE|Vfs::READABLE,$stat)) // no access !Vfs::check_access($url,Vfs::EXECUTABLE|Vfs::READABLE,$stat)) // no access
{ {
@ -1078,7 +1125,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
* @param boolean $eacl_access =null allows extending classes to pass the value of their check_extended_acl() method (no lsb!) * @param boolean $eacl_access =null allows extending classes to pass the value of their check_extended_acl() method (no lsb!)
* @return array * @return array
*/ */
static function url_stat ( $url, $flags, $eacl_access=null ) function url_stat ( $url, $flags, $eacl_access=null )
{ {
static $max_subquery_depth=null; static $max_subquery_depth=null;
if (is_null($max_subquery_depth)) if (is_null($max_subquery_depth))
@ -1187,7 +1234,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
error_log(__METHOD__."() decremented max_subquery_depth to $max_subquery_depth"); error_log(__METHOD__."() decremented max_subquery_depth to $max_subquery_depth");
Api\Config::save_value('max_subquery_depth', $max_subquery_depth, 'phpgwapi'); Api\Config::save_value('max_subquery_depth', $max_subquery_depth, 'phpgwapi');
if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) $GLOBALS['egw']->invalidate_session_cache(); if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) $GLOBALS['egw']->invalidate_session_cache();
return self::url_stat($url, $flags, $eacl_access); return $this->url_stat($url, $flags, $eacl_access);
} }
self::$stat_cache[$path] = $info; self::$stat_cache[$path] = $info;
@ -1284,7 +1331,8 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
*/ */
static function readlink($path) static function readlink($path)
{ {
$link = !($lstat = self::url_stat($path,STREAM_URL_STAT_LINK)) || is_null($lstat['readlink']) ? false : $lstat['readlink']; $vfs = new self();
$link = !($lstat = $vfs->url_stat($path,STREAM_URL_STAT_LINK)) || is_null($lstat['readlink']) ? false : $lstat['readlink'];
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = $link"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = $link");
@ -1302,7 +1350,8 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
{ {
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$target','$link')"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$target','$link')");
if (self::url_stat($link,0)) $vfs = new self();
if ($vfs->url_stat($link,0))
{ {
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$target','$link') $link exists, returning false!"); if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$target','$link') $link exists, returning false!");
return false; // $link already exists return false; // $link already exists
@ -1422,7 +1471,8 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
} }
if (is_null($fs_id)) if (is_null($fs_id))
{ {
if (!($stat = self::url_stat($path,0))) $vfs = new self();
if (!($stat = $vfs->url_stat($path,0)))
{ {
if (self::LOG_LEVEL) error_log(__METHOD__."($path,$rights,$owner,$fs_id) no such file or directory!"); if (self::LOG_LEVEL) error_log(__METHOD__."($path,$rights,$owner,$fs_id) no such file or directory!");
return false; // $path not found return false; // $path not found
@ -1740,7 +1790,7 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
if (self::LOG_LEVEL > 1) error_log(__METHOD__."(".array2string($path).','.array2string($props)); if (self::LOG_LEVEL > 1) error_log(__METHOD__."(".array2string($path).','.array2string($props));
if (!is_numeric($path)) if (!is_numeric($path))
{ {
if (!($stat = self::url_stat($path,0))) if (!($stat = $vfs->url_stat($path,0)))
{ {
return false; return false;
} }
@ -1807,7 +1857,8 @@ class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface
{ {
if (!is_numeric($id)) if (!is_numeric($id))
{ {
if (!($stat = self::url_stat($id,0))) $vfs = new self();
if (!($stat = $vfs->url_stat($id,0)))
{ {
if (self::LOG_LEVEL) error_log(__METHOD__."(".array2string($path_ids).",$ns) path '$id' not found!"); if (self::LOG_LEVEL) error_log(__METHOD__."(".array2string($path_ids).",$ns) path '$id' not found!");
return false; return false;

View File

@ -150,9 +150,10 @@ class Utils extends StreamWrapper
); );
$stmt = $delete_stmt = null; $stmt = $delete_stmt = null;
$msgs = array(); $msgs = array();
$sqlfs = Vfs\Sqlfs();
foreach($dirs as $path => $id) foreach($dirs as $path => $id)
{ {
if (!($stat = self::url_stat($path, STREAM_URL_STAT_LINK))) if (!($stat = $sqlfs->url_stat($path, STREAM_URL_STAT_LINK)))
{ {
if ($check_only) if ($check_only)
{ {
@ -308,6 +309,7 @@ class Utils extends StreamWrapper
{ {
$lostnfound = null; $lostnfound = null;
$msgs = array(); $msgs = array();
$sqlfs = Vfs\Sqlfs();
foreach(self::$pdo->query('SELECT fs.* FROM '.self::TABLE.' fs'. foreach(self::$pdo->query('SELECT fs.* FROM '.self::TABLE.' fs'.
' LEFT JOIN '.self::TABLE.' dir ON dir.fs_id=fs.fs_dir'. ' LEFT JOIN '.self::TABLE.' dir ON dir.fs_id=fs.fs_dir'.
' WHERE fs.fs_id > 1 AND dir.fs_id IS NULL') as $row) ' WHERE fs.fs_id > 1 AND dir.fs_id IS NULL') as $row)
@ -322,13 +324,13 @@ class Utils extends StreamWrapper
if (!isset($lostnfound)) if (!isset($lostnfound))
{ {
// check if we already have /lost+found, create it if not // check if we already have /lost+found, create it if not
if (!($lostnfound = self::url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET))) if (!($lostnfound = $sqlfs->url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET)))
{ {
Vfs::$is_root = true; Vfs::$is_root = true;
if (!self::mkdir(self::LOST_N_FOUND, self::LOST_N_FOUND_MOD, 0) || if (!self::mkdir(self::LOST_N_FOUND, self::LOST_N_FOUND_MOD, 0) ||
!(!($admins = $GLOBALS['egw']->accounts->name2id(self::LOST_N_FOUND_GRP)) || !(!($admins = $GLOBALS['egw']->accounts->name2id(self::LOST_N_FOUND_GRP)) ||
self::chgrp(self::LOST_N_FOUND, $admins) && self::chmod(self::LOST_N_FOUND,self::LOST_N_FOUND_MOD)) || self::chgrp(self::LOST_N_FOUND, $admins) && self::chmod(self::LOST_N_FOUND,self::LOST_N_FOUND_MOD)) ||
!($lostnfound = self::url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET))) !($lostnfound = $sqlfs->url_stat(self::LOST_N_FOUND, STREAM_URL_STAT_QUIET)))
{ {
$msgs[] = lang("Can't create directory %1 to connect found unconnected nodes to it!",self::LOST_N_FOUND); $msgs[] = lang("Can't create directory %1 to connect found unconnected nodes to it!",self::LOST_N_FOUND);
} }

View File

@ -157,11 +157,11 @@ class StreamWrapper implements StreamWrapperIface
* @param array|boolean &$stat=null on return: stat of existing file or false for non-existing files * @param array|boolean &$stat=null on return: stat of existing file or false for non-existing files
* @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry * @return string|boolean false if the url cant be resolved, should not happen if fstab has a root entry
*/ */
static function resolve_url_symlinks($_path,$file_exists=true,$resolve_last_symlink=true,&$stat=null) function resolve_url_symlinks($_path,$file_exists=true,$resolve_last_symlink=true,&$stat=null)
{ {
$path = self::get_path($_path); $path = self::get_path($_path);
if (!($stat = self::url_stat($path,$resolve_last_symlink?0:STREAM_URL_STAT_LINK)) && !$file_exists) if (!($stat = $this->url_stat($path,$resolve_last_symlink?0:STREAM_URL_STAT_LINK)) && !$file_exists)
{ {
$url = null; $url = null;
$stat = self::check_symlink_components($path,0,$url); $stat = self::check_symlink_components($path,0,$url);
@ -172,9 +172,9 @@ class StreamWrapper implements StreamWrapperIface
$url = $stat['url']; $url = $stat['url'];
} }
// if the url resolves to a symlink to the vfs, resolve this vfs:// url direct // if the url resolves to a symlink to the vfs, resolve this vfs:// url direct
if ($url && self::parse_url($url,PHP_URL_SCHEME) == self::SCHEME) if ($url && Vfs::parse_url($url,PHP_URL_SCHEME) == self::SCHEME)
{ {
$url = self::resolve_url(self::parse_url($url,PHP_URL_PATH)); $url = self::resolve_url(Vfs::parse_url($url,PHP_URL_PATH));
} }
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path,file_exists=$file_exists,resolve_last_symlink=$resolve_last_symlink) = '$url'$log"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path,file_exists=$file_exists,resolve_last_symlink=$resolve_last_symlink) = '$url'$log");
return $url; return $url;
@ -223,7 +223,7 @@ class StreamWrapper implements StreamWrapperIface
'home' => str_replace(array('\\\\','\\'),array('','/'),$GLOBALS['egw_info']['user']['homedirectory']), 'home' => str_replace(array('\\\\','\\'),array('','/'),$GLOBALS['egw_info']['user']['homedirectory']),
); );
} }
$parts = array_merge(self::parse_url($path),$defaults); $parts = array_merge(Vfs::parse_url($path),$defaults);
if (!$parts['host']) $parts['host'] = 'default'; // otherwise we get an invalid url (scheme:///path/to/something)! if (!$parts['host']) $parts['host'] = 'default'; // otherwise we get an invalid url (scheme:///path/to/something)!
if (!empty($parts['scheme']) && $parts['scheme'] != self::SCHEME) if (!empty($parts['scheme']) && $parts['scheme'] != self::SCHEME)
@ -237,7 +237,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
if ($mounted == '/' || $mounted == $parts['path'] || $mounted.'/' == substr($parts['path'],0,strlen($mounted)+1)) if ($mounted == '/' || $mounted == $parts['path'] || $mounted.'/' == substr($parts['path'],0,strlen($mounted)+1))
{ {
$scheme = self::parse_url($url,PHP_URL_SCHEME); $scheme = Vfs::parse_url($url,PHP_URL_SCHEME);
if (is_null(self::$wrappers) || !in_array($scheme,self::$wrappers)) if (is_null(self::$wrappers) || !in_array($scheme,self::$wrappers))
{ {
self::load_wrapper($scheme); self::load_wrapper($scheme);
@ -316,7 +316,7 @@ class StreamWrapper implements StreamWrapperIface
$this->opened_stream = null; $this->opened_stream = null;
$stat = null; $stat = null;
if (!($url = self::resolve_url_symlinks($path,$mode[0]=='r',true,$stat))) if (!($url = $this->resolve_url_symlinks($path,$mode[0]=='r',true,$stat)))
{ {
return false; return false;
} }
@ -330,7 +330,7 @@ class StreamWrapper implements StreamWrapperIface
return false; return false;
} }
$this->opened_stream_mode = $mode; $this->opened_stream_mode = $mode;
$this->opened_stream_path = $path[0] == '/' ? $path : self::parse_url($path, PHP_URL_PATH); $this->opened_stream_path = $path[0] == '/' ? $path : Vfs::parse_url($path, PHP_URL_PATH);
$this->opened_stream_url = $url; $this->opened_stream_url = $url;
$this->opened_stream_is_new = !$stat; $this->opened_stream_is_new = !$stat;
@ -480,6 +480,55 @@ class StreamWrapper implements StreamWrapperIface
return fstat($this->opened_stream); return fstat($this->opened_stream);
} }
/**
* StreamWrapper method (PHP 5.4+) for touch, chmod, chown and chgrp
*
* @param string $path
* @param int $option STREAM_META_(TOUCH|ACCESS|((OWNER|GROUP)(_NAME)?))
* @param array|int|string $value
* - STREAM_META_TOUCH array($time, $atime)
* - STREAM_META_ACCESS int
* - STREAM_(OWNER|GROUP) int
* - STREAM_(OWNER|GROUP)_NAME string
* @return boolean true on success, false on failure
*/
function stream_metadata($path, $option, $value)
{
if (!($url = $this->resolve_url_symlinks($path, $option == STREAM_META_TOUCH, false))) // true,false file need to exist, but do not resolve last component
{
return false;
}
if (self::url_is_readonly($url))
{
return false;
}
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path', $option, ".array2string($value).") url=$url");
switch($option)
{
case STREAM_META_TOUCH:
return touch($url, $value[0]); // atime is not supported
case STREAM_META_ACCESS:
return chmod($url, $value);
case STREAM_META_OWNER_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'u')) === false)
return false;
// fall through
case STREAM_META_OWNER:
return chown($url, $value);
case STREAM_META_GROUP_NAME:
if (($value = $GLOBALS['egw']->account->name2id($value, 'account_lid', 'g')) === false)
return false;
// fall through
case STREAM_META_GROUP:
return chgrp($url, $value);
}
return false;
}
/** /**
* This method is called in response to unlink() calls on URL paths associated with the wrapper. * This method is called in response to unlink() calls on URL paths associated with the wrapper.
* *
@ -489,9 +538,9 @@ class StreamWrapper implements StreamWrapperIface
* @param string $path * @param string $path
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function unlink ( $path ) function unlink ( $path )
{ {
if (!($url = self::resolve_url_symlinks($path,true,false))) // true,false file need to exist, but do not resolve last component if (!($url = $this->resolve_url_symlinks($path,true,false))) // true,false file need to exist, but do not resolve last component
{ {
return false; return false;
} }
@ -499,7 +548,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
return false; return false;
} }
$stat = self::url_stat($path, STREAM_URL_STAT_LINK); $stat = $this->url_stat($path, STREAM_URL_STAT_LINK);
self::symlinkCache_remove($path); self::symlinkCache_remove($path);
$ok = unlink($url); $ok = unlink($url);
@ -509,7 +558,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
Api\Hooks::process(array( Api\Hooks::process(array(
'location' => 'vfs_unlink', 'location' => 'vfs_unlink',
'path' => $path[0] == '/' ? $path : self::parse_url($path, PHP_URL_PATH), 'path' => $path[0] == '/' ? $path : Vfs::parse_url($path, PHP_URL_PATH),
'url' => $url, 'url' => $url,
'stat' => $stat, 'stat' => $stat,
),'',true); ),'',true);
@ -529,15 +578,15 @@ class StreamWrapper implements StreamWrapperIface
* @param string $path_to * @param string $path_to
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function rename ( $path_from, $path_to ) function rename ( $path_from, $path_to )
{ {
if (!($url_from = self::resolve_url_symlinks($path_from,true,false)) || if (!($url_from = $this->resolve_url_symlinks($path_from,true,false)) ||
!($url_to = self::resolve_url_symlinks($path_to,false))) !($url_to = $this->resolve_url_symlinks($path_to,false)))
{ {
return false; return false;
} }
// if file is moved from one filesystem / wrapper to an other --> copy it (rename fails cross wrappers) // if file is moved from one filesystem / wrapper to an other --> copy it (rename fails cross wrappers)
if (self::parse_url($url_from,PHP_URL_SCHEME) == self::parse_url($url_to,PHP_URL_SCHEME)) if (Vfs::parse_url($url_from,PHP_URL_SCHEME) == Vfs::parse_url($url_to,PHP_URL_SCHEME))
{ {
self::symlinkCache_remove($path_from); self::symlinkCache_remove($path_from);
$ret = rename($url_from,$url_to); $ret = rename($url_from,$url_to);
@ -547,7 +596,7 @@ class StreamWrapper implements StreamWrapperIface
$ret = stream_copy_to_stream($from,$to) !== false; $ret = stream_copy_to_stream($from,$to) !== false;
fclose($from); fclose($from);
fclose($to); fclose($to);
if ($ret) self::unlink($path_from); if ($ret) $this->unlink($path_from);
} }
else else
{ {
@ -562,8 +611,8 @@ class StreamWrapper implements StreamWrapperIface
{ {
Api\Hooks::process(array( Api\Hooks::process(array(
'location' => 'vfs_rename', 'location' => 'vfs_rename',
'from' => $path_from[0] == '/' ? $path_from : self::parse_url($path_from, PHP_URL_PATH), 'from' => $path_from[0] == '/' ? $path_from : Vfs::parse_url($path_from, PHP_URL_PATH),
'to' => $path_to[0] == '/' ? $path_to : self::parse_url($path_to, PHP_URL_PATH), 'to' => $path_to[0] == '/' ? $path_to : Vfs::parse_url($path_to, PHP_URL_PATH),
'url_from' => $url_from, 'url_from' => $url_from,
'url_to' => $url_to, 'url_to' => $url_to,
),'',true); ),'',true);
@ -582,9 +631,9 @@ class StreamWrapper implements StreamWrapperIface
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function mkdir ( $path, $mode, $options ) function mkdir ( $path, $mode, $options )
{ {
if (!($url = self::resolve_url_symlinks($path,false))) // false = directory does not need to exists if (!($url = $this->resolve_url_symlinks($path,false))) // false = directory does not need to exists
{ {
return false; return false;
} }
@ -595,7 +644,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
Api\Hooks::process(array( Api\Hooks::process(array(
'location' => 'vfs_mkdir', 'location' => 'vfs_mkdir',
'path' => $path[0] == '/' ? $path : self::parse_url($path, PHP_URL_PATH), 'path' => $path[0] == '/' ? $path : Vfs::parse_url($path, PHP_URL_PATH),
'url' => $url, 'url' => $url,
),'',true); ),'',true);
} }
@ -612,10 +661,10 @@ class StreamWrapper implements StreamWrapperIface
* @param int $options Possible values include STREAM_REPORT_ERRORS. * @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure. * @return boolean TRUE on success or FALSE on failure.
*/ */
static function rmdir ( $path, $options ) function rmdir ( $path, $options )
{ {
unset($options); // not uses but required by function signature unset($options); // not uses but required by function signature
if (!($url = self::resolve_url_symlinks($path))) if (!($url = $this->resolve_url_symlinks($path)))
{ {
return false; return false;
} }
@ -623,7 +672,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
return false; return false;
} }
$stat = self::url_stat($path, STREAM_URL_STAT_LINK); $stat = $this->url_stat($path, STREAM_URL_STAT_LINK);
self::symlinkCache_remove($path); self::symlinkCache_remove($path);
$ok = rmdir($url); $ok = rmdir($url);
@ -633,7 +682,7 @@ class StreamWrapper implements StreamWrapperIface
{ {
Api\Hooks::process(array( Api\Hooks::process(array(
'location' => 'vfs_rmdir', 'location' => 'vfs_rmdir',
'path' => $path[0] == '/' ? $path : self::parse_url($path, PHP_URL_PATH), 'path' => $path[0] == '/' ? $path : Vfs::parse_url($path, PHP_URL_PATH),
'url' => $url, 'url' => $url,
'stat' => $stat, 'stat' => $stat,
),'',true); ),'',true);
@ -641,217 +690,6 @@ class StreamWrapper implements StreamWrapperIface
return $ok; return $ok;
} }
/**
* 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
* @return mixed return value of backend or false if function does not exist on backend
*/
static protected function _call_on_backend($name,$params,$fail_silent=false,$path_param_key=0)
{
$pathes = $params[$path_param_key];
$scheme2urls = array();
foreach(is_array($pathes) ? $pathes : array($pathes) as $path)
{
if (!($url = self::resolve_url_symlinks($path,false,false)))
{
return false;
}
$k=(string)self::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 = self::scheme2class($scheme)) || !method_exists($class,$name))
{
if (!$fail_silent) trigger_error("Can't $name for scheme $scheme!\n",E_USER_WARNING);
return false;
}
if (!is_array($pathes))
{
$params[$path_param_key] = $url;
return call_user_func_array(array($class,$name),$params);
}
$params[$path_param_key] = $urls;
if (!is_array($r = call_user_func_array(array($class,$name),$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=self::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;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* @param string $path
* @param int $time =null modification time (unix timestamp), default null = current time
* @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!
* @return boolean true on success, false otherwise
*/
static function touch($path,$time=null,$atime=null)
{
return self::_call_on_backend('touch',array($path,$time,$atime));
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Requires owner or root rights!
*
* @param string $path
* @param string $mode mode string see Vfs::mode2int
* @return boolean true on success, false otherwise
*/
static function chmod($path,$mode)
{
return self::_call_on_backend('chmod',array($path,$mode));
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Requires root rights!
*
* @param string $path
* @param int $owner numeric user id
* @return boolean true on success, false otherwise
*/
static function chown($path,$owner)
{
return self::_call_on_backend('chown',array($path,$owner));
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* Requires owner or root rights!
*
* @param string $path
* @param int $group numeric group id
* @return boolean true on success, false otherwise
*/
static function chgrp($path,$group)
{
return self::_call_on_backend('chgrp',array($path,$group));
}
/**
* Returns the target of a symbolic link
*
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* @param string $path
* @return string|boolean link-data or false if no link
*/
static function readlink($path)
{
$ret = self::_call_on_backend('readlink',array($path),true); // true = fail silent, if backend does not support readlink
//error_log(__METHOD__."('$path') returning ".array2string($ret).' '.function_backtrace());
return $ret;
}
/**
* Creates a symbolic link
*
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* @param string $target target of the link
* @param string $link path of the link to create
* @return boolean true on success, false on error
*/
static function symlink($target,$link)
{
if (($ret = self::_call_on_backend('symlink',array($target,$link),false,1))) // 1=path is in $link!
{
self::symlinkCache_remove($link);
}
return $ret;
}
/**
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
*
* The methods use the following ways to get the mime type (in that order)
* - directories (is_dir()) --> self::DIR_MIME_TYPE
* - stream implemented by class defining the STAT_RETURN_MIME_TYPE constant --> use mime-type returned by url_stat
* - for regular filesystem use mime_content_type function if available
* - use eGW's mime-magic class
*
* @param string $path
* @param boolean $recheck =false true = do a new check, false = rely on stored mime type (if existing)
* @return string mime-type (self::DIR_MIME_TYPE for directories)
*/
static function mime_content_type($path,$recheck=false)
{
if (!($url = self::resolve_url_symlinks($path)))
{
return false;
}
if (($scheme = self::parse_url($url,PHP_URL_SCHEME)) && !$recheck)
{
// check it it's an eGW stream wrapper returning mime-type via url_stat
// we need to first check if the constant is defined, as we get a fatal error in php5.3 otherwise
if (class_exists($class = self::scheme2class($scheme)) &&
defined($class.'::STAT_RETURN_MIME_TYPE') &&
($mime_attr = constant($class.'::STAT_RETURN_MIME_TYPE')))
{
$stat = call_user_func(array($class,'url_stat'),self::parse_url($url,PHP_URL_PATH),0);
if ($stat && $stat[$mime_attr])
{
$mime = $stat[$mime_attr];
}
}
}
if (!$mime && is_dir($url))
{
$mime = self::DIR_MIME_TYPE;
}
// if we operate on the regular filesystem and the mime_content_type function is available --> use it
if (!$mime && !$scheme && function_exists('mime_content_type'))
{
$mime = mime_content_type($path);
}
// using EGw's own mime magic (currently only checking the extension!)
if (!$mime)
{
$mime = Api\MimeMagic::filename2mime(self::parse_url($url,PHP_URL_PATH));
}
//error_log(__METHOD__."($path,$recheck) mime=$mime");
return $mime;
}
/** /**
* This method is called immediately when your stream object is created for examining directory contents with opendir(). * This method is called immediately when your stream object is created for examining directory contents with opendir().
* *
@ -864,7 +702,7 @@ class StreamWrapper implements StreamWrapperIface
$this->dir_url_params = array(); $this->dir_url_params = array();
$this->extra_dir_ptr = 0; $this->extra_dir_ptr = 0;
if (!($this->opened_dir_url = self::resolve_url_symlinks($path))) if (!($this->opened_dir_url = $this->resolve_url_symlinks($path)))
{ {
if (self::LOG_LEVEL > 0) error_log(__METHOD__."( $path,$options) resolve_url_symlinks() failed!"); if (self::LOG_LEVEL > 0) error_log(__METHOD__."( $path,$options) resolve_url_symlinks() failed!");
return false; return false;
@ -877,7 +715,7 @@ class StreamWrapper implements StreamWrapperIface
} }
$this->opened_dir_writable = Vfs::check_access($this->opened_dir_url,Vfs::WRITABLE); $this->opened_dir_writable = Vfs::check_access($this->opened_dir_url,Vfs::WRITABLE);
// check our fstab if we need to add some of the mountpoints // check our fstab if we need to add some of the mountpoints
$basepath = self::parse_url($path,PHP_URL_PATH); $basepath = Vfs::parse_url($path,PHP_URL_PATH);
foreach(array_keys(self::$fstab) as $mounted) foreach(array_keys(self::$fstab) as $mounted)
{ {
if (((Vfs::dirname($mounted) == $basepath || Vfs::dirname($mounted).'/' == $basepath) && $mounted != '/') && if (((Vfs::dirname($mounted) == $basepath || Vfs::dirname($mounted).'/' == $basepath) && $mounted != '/') &&
@ -922,7 +760,7 @@ class StreamWrapper implements StreamWrapperIface
* @param boolean $check_symlink_components =true check if path contains symlinks in path components other then the last one * @param boolean $check_symlink_components =true check if path contains symlinks in path components other then the last one
* @return array * @return array
*/ */
static function url_stat ( $path, $flags, $try_create_home=false, $check_symlink_components=true, $check_symlink_depth=self::MAX_SYMLINK_DEPTH, $try_reconnect=true ) function url_stat ( $path, $flags, $try_create_home=false, $check_symlink_components=true, $check_symlink_depth=self::MAX_SYMLINK_DEPTH, $try_reconnect=true )
{ {
if (!($url = self::resolve_url($path,!($flags & STREAM_URL_STAT_LINK), $check_symlink_components))) if (!($url = self::resolve_url($path,!($flags & STREAM_URL_STAT_LINK), $check_symlink_components)))
{ {
@ -946,17 +784,17 @@ class StreamWrapper implements StreamWrapperIface
if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path',$flags) maximum symlink depth exceeded, might be a circular symlink!"); if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$path',$flags) maximum symlink depth exceeded, might be a circular symlink!");
$stat = false; $stat = false;
} }
elseif (($lpath = self::readlink($url))) elseif (($lpath = Vfs::readlink($url)))
{ {
if ($lpath[0] != '/') // concat relative path if ($lpath[0] != '/') // concat relative path
{ {
$lpath = Vfs::concat(self::parse_url($path,PHP_URL_PATH),'../'.$lpath); $lpath = Vfs::concat(Vfs::parse_url($path,PHP_URL_PATH),'../'.$lpath);
} }
$u_query = parse_url($url,PHP_URL_QUERY); $u_query = parse_url($url,PHP_URL_QUERY);
$url = Vfs::PREFIX.$lpath; $url = Vfs::PREFIX.$lpath;
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path,$flags) symlif (substr($path,-1) == '/' && $path != '/') $path = substr($path,0,-1); // remove trailing slash eg. added by WebDAVink found and resolved to $url"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path,$flags) symlif (substr($path,-1) == '/' && $path != '/') $path = substr($path,0,-1); // remove trailing slash eg. added by WebDAVink found and resolved to $url");
// try reading the stat of the link // try reading the stat of the link
if (($stat = self::url_stat($lpath, STREAM_URL_STAT_QUIET, false, true, $check_symlink_depth-1))) if (($stat = $this->url_stat($lpath, STREAM_URL_STAT_QUIET, false, true, $check_symlink_depth-1)))
{ {
$stat_query = parse_url($stat['url'], PHP_URL_QUERY); $stat_query = parse_url($stat['url'], PHP_URL_QUERY);
if($u_query || $stat_query) if($u_query || $stat_query)
@ -982,13 +820,13 @@ class StreamWrapper implements StreamWrapperIface
{ {
// reconnect to db // reconnect to db
Vfs\Sqlfs\StreamWrapper::reconnect(); Vfs\Sqlfs\StreamWrapper::reconnect();
return self::url_stat($path, $flags, $try_create_home, $check_symlink_components, $check_symlink_depth, false); return $this->url_stat($path, $flags, $try_create_home, $check_symlink_components, $check_symlink_depth, false);
} }
// if numer of tries is exceeded, re-throw exception // if numer of tries is exceeded, re-throw exception
throw $e; throw $e;
} }
// check if a failed url_stat was for a home dir, in that case silently create it // check if a failed url_stat was for a home dir, in that case silently create it
if (!$stat && $try_create_home && Vfs::dirname(self::parse_url($path,PHP_URL_PATH)) == '/home' && if (!$stat && $try_create_home && Vfs::dirname(Vfs::parse_url($path,PHP_URL_PATH)) == '/home' &&
($id = $GLOBALS['egw']->accounts->name2id(basename($path))) && ($id = $GLOBALS['egw']->accounts->name2id(basename($path))) &&
$GLOBALS['egw']->accounts->id2name($id) == basename($path)) // make sure path has the right case! $GLOBALS['egw']->accounts->id2name($id) == basename($path)) // make sure path has the right case!
{ {
@ -1000,7 +838,7 @@ class StreamWrapper implements StreamWrapperIface
); );
call_user_func(array(__NAMESPACE__.'\\Hooks',$hook_data['location']),$hook_data); call_user_func(array(__NAMESPACE__.'\\Hooks',$hook_data['location']),$hook_data);
unset($hook_data); unset($hook_data);
$stat = self::url_stat($path,$flags,false); $stat = $this->url_stat($path,$flags,false);
} }
$query = parse_url($url, PHP_URL_QUERY); $query = parse_url($url, PHP_URL_QUERY);
if (!$stat && $check_symlink_components) // check if there's a symlink somewhere inbetween the path if (!$stat && $check_symlink_components) // check if there's a symlink somewhere inbetween the path
@ -1043,7 +881,7 @@ class StreamWrapper implements StreamWrapperIface
* @param string &$url=null already resolved path * @param string &$url=null already resolved path
* @return array|boolean stat array or false if not found * @return array|boolean stat array or false if not found
*/ */
static private function check_symlink_components($path,$flags=0,&$url=null) private function check_symlink_components($path,$flags=0,&$url=null)
{ {
if (is_null($url) && !($url = self::resolve_url($path))) if (is_null($url) && !($url = self::resolve_url($path)))
{ {
@ -1055,20 +893,20 @@ class StreamWrapper implements StreamWrapperIface
while (($rel_path = Vfs::basename($url).($rel_path ? '/'.$rel_path : '')) && while (($rel_path = Vfs::basename($url).($rel_path ? '/'.$rel_path : '')) &&
($url = Vfs::dirname($url))) ($url = Vfs::dirname($url)))
{ {
if (($stat = self::url_stat($url,0,false,false))) if (($stat = $this->url_stat($url,0,false,false)))
{ {
if (is_link($url) && ($lpath = self::readlink($url))) if (is_link($url) && ($lpath = Vfs::readlink($url)))
{ {
if (self::LOG_LEVEL > 1) $log = "rel_path='$rel_path', url='$url': lpath='$lpath'"; if (self::LOG_LEVEL > 1) $log = "rel_path='$rel_path', url='$url': lpath='$lpath'";
if ($lpath[0] != '/') if ($lpath[0] != '/')
{ {
$lpath = Vfs::concat(self::parse_url($url,PHP_URL_PATH),'../'.$lpath); $lpath = Vfs::concat(Vfs::parse_url($url,PHP_URL_PATH),'../'.$lpath);
} }
//self::symlinkCache_add($path,Vfs::PREFIX.$lpath); //self::symlinkCache_add($path,Vfs::PREFIX.$lpath);
$url = Vfs::PREFIX.Vfs::concat($lpath,$rel_path); $url = Vfs::PREFIX.Vfs::concat($lpath,$rel_path);
if (self::LOG_LEVEL > 1) error_log("$log --> lpath='$lpath', url='$url'"); if (self::LOG_LEVEL > 1) error_log("$log --> lpath='$lpath', url='$url'");
return self::url_stat($url,$flags); return $this->url_stat($url,$flags);
} }
$url = Vfs::concat($url,$rel_path); $url = Vfs::concat($url,$rel_path);
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path',$flags,'$url') returning null"); if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path',$flags,'$url') returning null");
@ -1098,7 +936,7 @@ class StreamWrapper implements StreamWrapperIface
if (isset(self::$symlink_cache[$path])) return; // nothing to do if (isset(self::$symlink_cache[$path])) return; // nothing to do
if ($target[0] != '/') $target = self::parse_url($target,PHP_URL_PATH); if ($target[0] != '/') $target = Vfs::parse_url($target,PHP_URL_PATH);
self::$symlink_cache[$path] = $target; self::$symlink_cache[$path] = $target;
@ -1162,17 +1000,9 @@ class StreamWrapper implements StreamWrapperIface
* Clears our internal stat and symlink cache * Clears our internal stat and symlink cache
* *
* Normaly not necessary, as it is automatically cleared/updated, UNLESS Vfs::$user changes! * Normaly not necessary, as it is automatically cleared/updated, UNLESS Vfs::$user changes!
*
* We have to clear the symlink cache before AND after calling the backend,
* because auf traversal rights may be different when Vfs::$user changes!
*
* @param string $path ='/' path of backend, whos cache to clear
*/ */
static function clearstatcache($path='/') static function clearstatcache()
{ {
//error_log(__METHOD__."('$path')");
self::$symlink_cache = self::$resolve_url_cache = array();
self::_call_on_backend('clearstatcache', array($path), true, 0);
self::$symlink_cache = self::$resolve_url_cache = array(); self::$symlink_cache = self::$resolve_url_cache = array();
} }
@ -1310,59 +1140,6 @@ class StreamWrapper implements StreamWrapperIface
} }
} }
/**
* Utf-8 save version of parse_url
*
* Does caching withing request, to not have to parse urls over and over again.
*
* @param string $url
* @param int $component =-1 PHP_URL_* constants
* @return array|string|boolean on success array or string, if $component given, or false on failure
*/
static function parse_url($url, $component=-1)
{
static $component2str = array(
PHP_URL_SCHEME => 'scheme',
PHP_URL_HOST => 'host',
PHP_URL_PORT => 'port',
PHP_URL_USER => 'user',
PHP_URL_PASS => 'pass',
PHP_URL_PATH => 'path',
PHP_URL_QUERY => 'query',
PHP_URL_FRAGMENT => 'fragment',
);
static $cache = array(); // some caching
$result =& $cache[$url];
if (!isset($result))
{
// Build arrays of values we need to decode before parsing
static $entities = array('%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%24', '%2C', '%2F', '%3F', '%23', '%5B', '%5D');
static $replacements = array('!', '*', "'", "(", ")", ";", ":", "@", "&", "=", "$", ",", "/", "?", "#", "[", "]");
static $str_replace = null;
if (!isset($str_replace)) $str_replace = function_exists('mb_str_replace') ? 'mb_str_replace' : 'str_replace';
// Create encoded URL with special URL characters decoded so it can be parsed
// All other characters will be encoded
$encodedURL = $str_replace($entities, $replacements, urlencode($url));
// Parse the encoded URL
$result = $encodedParts = parse_url($encodedURL);
// Now, decode each value of the resulting array
if ($encodedParts)
{
$result = array();
foreach ($encodedParts as $key => $value)
{
$result[$key] = urldecode($str_replace($replacements, $entities, $value));
}
}
}
return $component >= 0 ? $result[$component2str[$component]] : $result;
}
/** /**
* Getting the path from an url (or path) AND removing trailing slashes * Getting the path from an url (or path) AND removing trailing slashes
* *
@ -1372,14 +1149,14 @@ class StreamWrapper implements StreamWrapperIface
*/ */
static protected function get_path($path,$only_remove_scheme=self::SCHEME) static protected function get_path($path,$only_remove_scheme=self::SCHEME)
{ {
if ($path[0] != '/' && (!$only_remove_scheme || self::parse_url($path, PHP_URL_SCHEME) == $only_remove_scheme)) if ($path[0] != '/' && (!$only_remove_scheme || Vfs::parse_url($path, PHP_URL_SCHEME) == $only_remove_scheme))
{ {
$path = self::parse_url($path, PHP_URL_PATH); $path = Vfs::parse_url($path, PHP_URL_PATH);
} }
// remove trailing slashes eg. added by WebDAV, but do NOT remove / from "sqlfs://default/"! // remove trailing slashes eg. added by WebDAV, but do NOT remove / from "sqlfs://default/"!
if ($path != '/') if ($path != '/')
{ {
while (mb_substr($path, -1) == '/' && $path != '/' && ($path[0] == '/' || self::parse_url($path, PHP_URL_PATH) != '/')) while (mb_substr($path, -1) == '/' && $path != '/' && ($path[0] == '/' || Vfs::parse_url($path, PHP_URL_PATH) != '/'))
{ {
$path = mb_substr($path,0,-1); $path = mb_substr($path,0,-1);
} }
@ -1405,6 +1182,116 @@ class StreamWrapper implements StreamWrapperIface
return $ret; return $ret;
} }
/**
* Mounts $url under $path in the vfs, called without parameter it returns the fstab
*
* The fstab is stored in the eGW configuration and used for all eGW users.
*
* @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. /
* @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
* @param boolean $persitent_mount =true create a persitent mount, or only a temprary for current request
* @param boolean $clear_fstab =false true clear current fstab, false (default) only add given mount
* @return array|boolean array with fstab, if called without parameter or true on successful mount
*/
static function mount($url=null,$path=null,$check_url=null,$persitent_mount=true,$clear_fstab=false)
{
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');
if (isset($api_config['vfs_fstab']) && is_array($api_config['vfs_fstab']))
{
self::$fstab = $api_config['vfs_fstab'];
}
else
{
self::$fstab = array(
'/' => 'sqlfs://$host/',
'/apps' => 'links://$host/apps',
);
}
unset($api_config);
}
if (is_null($url) || is_null($path))
{
if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') returns '.array2string(self::$fstab));
return self::$fstab;
}
if (!self::$is_root)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') permission denied, you are NOT root!');
return false; // only root can mount
}
if ($clear_fstab)
{
self::$fstab = array();
}
if (isset(self::$fstab[$path]) && self::$fstab[$path] === $url)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($url).','.array2string($path).') already mounted.');
return true; // already mounted
}
self::load_wrapper(Vfs::parse_url($url,PHP_URL_SCHEME));
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
}
self::$fstab[$path] = $url;
uksort(self::$fstab, function($a, $b)
{
return strlen($a) - strlen($b);
});
if ($persitent_mount)
{
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;
}
/**
* Unmounts a filesystem part of the vfs
*
* @param string $path url or path of the filesystem to unmount
*/
static function umount($path)
{
if (!self::$is_root)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($path).','.array2string($path).') permission denied, you are NOT root!');
return false; // only root can mount
}
if (!isset(self::$fstab[$path]) && ($path = array_search($path,self::$fstab)) === false)
{
if (self::LOG_LEVEL > 0) error_log(__METHOD__.'('.array2string($path).') NOT mounted!');
return false; // $path not mounted
}
unset(self::$fstab[$path]);
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($path).') returns true (successful unmount).');
return true;
}
/** /**
* Init our static properties and register this wrapper * Init our static properties and register this wrapper
* *

View File

@ -143,7 +143,7 @@ interface StreamWrapperIface
* @param string $path * @param string $path
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function unlink ( $path ); function unlink ( $path );
/** /**
* This method is called in response to rename() calls on URL paths associated with the wrapper. * This method is called in response to rename() calls on URL paths associated with the wrapper.
@ -157,7 +157,7 @@ interface StreamWrapperIface
* @param string $path_to * @param string $path_to
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function rename ( $path_from, $path_to ); function rename ( $path_from, $path_to );
/** /**
* This method is called in response to mkdir() calls on URL paths associated with the wrapper. * This method is called in response to mkdir() calls on URL paths associated with the wrapper.
@ -170,7 +170,7 @@ interface StreamWrapperIface
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
* @return boolean TRUE on success or FALSE on failure * @return boolean TRUE on success or FALSE on failure
*/ */
static function mkdir ( $path, $mode, $options ); function mkdir ( $path, $mode, $options );
/** /**
* This method is called in response to rmdir() calls on URL paths associated with the wrapper. * This method is called in response to rmdir() calls on URL paths associated with the wrapper.
@ -182,7 +182,7 @@ interface StreamWrapperIface
* @param int $options Possible values include STREAM_REPORT_ERRORS. * @param int $options Possible values include STREAM_REPORT_ERRORS.
* @return boolean TRUE on success or FALSE on failure. * @return boolean TRUE on success or FALSE on failure.
*/ */
static function rmdir ( $path, $options ); function rmdir ( $path, $options );
/** /**
* This method is called immediately when your stream object is created for examining directory contents with opendir(). * This method is called immediately when your stream object is created for examining directory contents with opendir().
@ -218,7 +218,7 @@ interface StreamWrapperIface
* stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!
* @return array * @return array
*/ */
static function url_stat ( $path, $flags ); function url_stat ( $path, $flags );
/** /**
* This method is called in response to readdir(). * This method is called in response to readdir().

View File

@ -423,10 +423,12 @@ else
// fix egw_cache evtl. created by root, stoping webserver from accessing it // fix egw_cache evtl. created by root, stoping webserver from accessing it
fix_perms(); fix_perms();
if (!empty($config['start_webserver']))
{
// restart running Apache, to force APC to update changed sources and/or Apache configuration // restart running Apache, to force APC to update changed sources and/or Apache configuration
$output = array(); $output = array();
run_cmd(build_cmd('start_webserver', 'status').' && '.build_cmd('start_webserver', 'restart'), $output, true); run_cmd(build_cmd('start_webserver', 'status').' && '.build_cmd('start_webserver', 'restart'), $output, true);
}
exit($ret); exit($ret);
} }
@ -577,7 +579,7 @@ function fix_perms()
{ {
global $config; global $config;
if (file_exists('/tmp/egw_cache')) if (file_exists('/tmp/egw_cache') && !empty($config['webserver_user']))
{ {
system('/bin/chown -R '.$config['webserver_user'].' /tmp/egw_cache'); system('/bin/chown -R '.$config['webserver_user'].' /tmp/egw_cache');
system('/bin/chmod 700 /tmp/egw_cache'); system('/bin/chmod 700 /tmp/egw_cache');