mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-22 07:53:39 +01:00
More improvments of the sqlfs code and the command line interface:
- read rights are not checks in each traversed directory (via sql in a single query to locate the path) - diropen additionally checks for execute rights - fopen checks for read or write depending on the mode - chmod, chgrp, chown methods in sqlfs and egw_vfs/vfs plus an egw_vfs::$is_root var used to grant root rights (no access controll and chown or chgrp without being the owner of a file) - find method (some more params to come) to recursivly search and optionaly execute some callback - egw_vfs::remove doing a "rm -r" / recursive remove or dirs and files - new files or dirs inherit the perms and ownership from the parent directory (no umask) - files/dirs the user has no read rights, in a directory where he has no write rights, get hidden (eg. not showing all the other users / groups home dirs - many new cli commands (chmod, chgrp, chown, find), recursive option for most commands and the ability to use it with root rights, see the usage message if called without options - "cp -r -p" to copy a whole tree incl. ownership and perms, eg. backing up /home to /backup
This commit is contained in:
parent
d81d9bce03
commit
8afe9094b7
@ -13,6 +13,8 @@
|
||||
|
||||
chdir(dirname(__FILE__)); // to enable our relative pathes to work
|
||||
|
||||
error_reporting(error_reporting() & ~E_NOTICE);
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) // security precaution: forbit calling ls as web-page
|
||||
{
|
||||
die('<h1>'.basename(__FILE__).' must NOT be called as web-page --> exiting !!!</h1>');
|
||||
@ -30,7 +32,7 @@ function user_pass_from_argv(&$account)
|
||||
//print_r($account);
|
||||
if (!($sessionid = $GLOBALS['egw']->session->create($account)))
|
||||
{
|
||||
echo "Wrong admin-account or -password !!!\n\n";
|
||||
echo "Wrong user-account or -password !!!\n\n";
|
||||
usage('',1);
|
||||
}
|
||||
return $sessionid;
|
||||
@ -45,19 +47,26 @@ function user_pass_from_argv(&$account)
|
||||
function usage($action=null,$ret=0)
|
||||
{
|
||||
$cmd = basename(__FILE__);
|
||||
echo "Usage:\t$cmd URL [URL2 ...]\n";
|
||||
echo "Usage:\t$cmd [-r|--recursive] URL [URL2 ...]\n";
|
||||
echo "\t$cmd --cat URL [URL2 ...]\n";
|
||||
echo "\t$cmd --cp URL-from URL-to\n";
|
||||
echo "\t$cmd --cp URL-from [URL-from2 ...] URL-to-directory\n";
|
||||
echo "\t$cmd --rm URL [URL2 ...]\n";
|
||||
echo "\t$cmd --cp [-r|--recursive] [-p|--perms] URL-from URL-to\n";
|
||||
echo "\t$cmd --cp [-r|--recursive] [-p|--perms] URL-from [URL-from2 ...] URL-to-directory\n";
|
||||
echo "\t$cmd --rm [-r|--recursive] URL [URL2 ...]\n";
|
||||
echo "\t$cmd --mkdir [-p|--parents] URL [URL2 ...]\n";
|
||||
echo "\t$cmd --rmdir URL [URL2 ...]\n";
|
||||
echo "\t$cmd --touch [-d|--date time] URL [URL2 ...]\n";
|
||||
echo "URL: oldvfs://user:password@domain/home/user/file, /dir/file, ...\n";
|
||||
echo "\t$cmd --touch [-r|--recursive] [-d|--date time] URL [URL2 ...]\n";
|
||||
echo "\t$cmd --chmod [-r|--recursive] mode=[ugoa]*[+-=][rwx]+,... URL [URL2 ...]\n";
|
||||
echo "\t$cmd --chown [-r|--recursive] user URL [URL2 ...]\n";
|
||||
echo "\t$cmd --chgrp [-r|--recursive] group URL [URL2 ...]\n";
|
||||
echo "\t$cmd --find URL [URL2 ...] [options to come]\n";
|
||||
|
||||
echo "\nURL: {vfs|sqlfs|oldvfs}://user:password@domain/home/user/file, /dir/file, ...\n";
|
||||
|
||||
echo "\nUse root_{header-admin|config-user} as user and according password for root access (no user specific access control and chown).\n\n";
|
||||
|
||||
exit;
|
||||
}
|
||||
$long = $numeric = $recursive = false;
|
||||
$long = $numeric = $recursive = $perms = false;
|
||||
$argv = $_SERVER['argv'];
|
||||
$cmd = basename(array_shift($argv),'.php');
|
||||
|
||||
@ -84,10 +93,20 @@ foreach($argv as $key => $option)
|
||||
continue 2; // switch is counting too!
|
||||
|
||||
case '-r': case '--recursive':
|
||||
case '-p': case '--parents':
|
||||
$recursive = true;
|
||||
continue 2; // switch is counting too!
|
||||
|
||||
case '-p': case '--parents': case '--perms':
|
||||
if ($cmd == 'cp')
|
||||
{
|
||||
$perms = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$recursive = true;
|
||||
}
|
||||
continue 2; // switch is counting too!
|
||||
|
||||
case '-d': case '--date':
|
||||
$time = strtotime($argv[$key+1]);
|
||||
unset($argv[$key+1]);
|
||||
@ -101,6 +120,10 @@ foreach($argv as $key => $option)
|
||||
case '--mkdir': // make directories
|
||||
case '--rename':// rename
|
||||
case '--touch': // touch
|
||||
case '--chmod': // chmod
|
||||
case '--chown': // chown (requires root)
|
||||
case '--chgrp': // chgrp
|
||||
case '--find':
|
||||
$cmd = substr($option,2);
|
||||
continue 2; // switch is counting too!
|
||||
}
|
||||
@ -110,8 +133,12 @@ $argc = count($argv);
|
||||
|
||||
switch($cmd)
|
||||
{
|
||||
case 'find':
|
||||
do_find($argv);
|
||||
break;
|
||||
|
||||
case 'cp':
|
||||
do_cp($argv);
|
||||
do_cp($argv,$recursive,$perms);
|
||||
break;
|
||||
|
||||
case 'rename':
|
||||
@ -122,15 +149,26 @@ switch($cmd)
|
||||
break;
|
||||
|
||||
default:
|
||||
while($url = array_shift($argv))
|
||||
while($argv)
|
||||
{
|
||||
$url = array_shift($argv);
|
||||
|
||||
load_wrapper($url);
|
||||
//echo "$cmd $url (long=".(int)$long.", numeric=".(int)$numeric.")\n";
|
||||
|
||||
switch($cmd)
|
||||
{
|
||||
case 'rm':
|
||||
if ($recursive && class_exists('egw_vfs'))
|
||||
{
|
||||
array_unshift($argv,$url);
|
||||
egw_vfs::remove($argv);
|
||||
$argv = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($url);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'rmdir':
|
||||
@ -142,28 +180,106 @@ switch($cmd)
|
||||
break;
|
||||
|
||||
case 'touch':
|
||||
case 'chmod':
|
||||
case 'chown':
|
||||
case 'chgrp':
|
||||
switch($cmd)
|
||||
{
|
||||
case 'touch':
|
||||
$params = array($url,$time);
|
||||
break;
|
||||
case 'chmod':
|
||||
if (!isset($mode))
|
||||
{
|
||||
$mode = $url; // first param is mode
|
||||
$url = array_shift($argv);
|
||||
}
|
||||
if (parse_url($url,PHP_URL_SCHEME)) load_wrapper($url); // cant use stat or egw_vfs::mode2int otherwise!
|
||||
|
||||
if (strpos($mode,'+') !== false || strpos($mode,'-') !== false)
|
||||
{
|
||||
$stat = stat($url);
|
||||
$set = $stat['mode'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$set = 0;
|
||||
}
|
||||
if (!class_exists('egw_vfs'))
|
||||
{
|
||||
die("chmod only implemented for eGW streams!"); // dont want to repeat the code here
|
||||
}
|
||||
$set = egw_vfs::mode2int($mode,$set);
|
||||
$params = array($url,$set);
|
||||
break;
|
||||
case 'chown':
|
||||
case 'chgrp':
|
||||
$type = $cmd == 'chgrp' ? 'group' : 'user';
|
||||
if (!isset($owner))
|
||||
{
|
||||
$owner = $url; // first param is owner/group
|
||||
$url = array_shift($argv);
|
||||
if (parse_url($url,PHP_URL_SCHEME)) load_wrapper($url); // we need the header loaded
|
||||
if ($owner == 'root')
|
||||
{
|
||||
$owner = 0;
|
||||
}
|
||||
elseif (!is_numeric($owner))
|
||||
{
|
||||
if (!is_object($GLOBALS['egw']))
|
||||
{
|
||||
die("only numeric user/group-id's allowed for non eGW streams!");
|
||||
}
|
||||
if (!($owner = $GLOBALS['egw']->accounts->name2id($owner_was=$owner,'account_lid',$type[0])) ||
|
||||
($owner < 0) != ($cmd == 'chgrp'))
|
||||
{
|
||||
die("Unknown $type '$owner_was'!");
|
||||
}
|
||||
}
|
||||
elseif($owner && is_object($GLOBALS['egw']) && (!$GLOBALS['egw']->accounts->id2name($owner) ||
|
||||
($owner < 0) != ($cmd == 'chgrp')))
|
||||
{
|
||||
die("Unknown $type '$owner_was'!");
|
||||
}
|
||||
}
|
||||
$params = array($url,$owner);
|
||||
break;
|
||||
}
|
||||
if (($scheme = parse_url($url,PHP_URL_SCHEME)))
|
||||
{
|
||||
load_wrapper($url);
|
||||
if (class_exists($class = $scheme.'_stream_wrapper') && method_exists($class,'touch'))
|
||||
{
|
||||
call_user_func(array($scheme.'_stream_wrapper','touch'),$url,$time);
|
||||
$cmd = array($scheme.'_stream_wrapper',$cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("Can't touch for scheme $scheme!\n");
|
||||
die("Can't $cmd for scheme $scheme!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
if ($recursive && class_exists('egw_vfs'))
|
||||
{
|
||||
touch($url,$time);
|
||||
array_unshift($argv,$url);
|
||||
$params = array($argv,null,$cmd,$params[1]);
|
||||
$cmd = array('egw_vfs','find');
|
||||
$argv = array(); // we processed all url's
|
||||
}
|
||||
//echo "calling cmd=".print_r($cmd,true).", params=".print_r($params,true)."\n";
|
||||
call_user_func_array($cmd,$params);
|
||||
break;
|
||||
|
||||
case 'cat':
|
||||
case 'ls':
|
||||
default:
|
||||
if (is_dir($url) && ($dir = opendir($url)))
|
||||
// recursive ls atm only for vfs://
|
||||
if ($cmd != 'cat' && $recursive && class_exists('egw_vfs'))
|
||||
{
|
||||
load_wrapper($url);
|
||||
array_unshift($argv,$url);
|
||||
egw_vfs::find($argv,array('dirs_last'=>true),'do_stat',array($long,$numeric,true));
|
||||
$argv = array();
|
||||
}
|
||||
elseif (is_dir($url) && ($dir = opendir($url)))
|
||||
{
|
||||
if ($argc)
|
||||
{
|
||||
@ -235,8 +351,30 @@ function load_wrapper($url)
|
||||
)
|
||||
);
|
||||
|
||||
if (substr($GLOBALS['egw_login_data']['login'],0,5) != 'root_')
|
||||
{
|
||||
include('../header.inc.php');
|
||||
}
|
||||
else
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['currentapp'] = 'login';
|
||||
include('../header.inc.php');
|
||||
|
||||
if ($GLOBALS['egw_login_data']['login'] == 'root_'.$GLOBALS['egw_info']['server']['header_admin_user'] &&
|
||||
_check_pw($GLOBALS['egw_info']['server']['header_admin_password'],$GLOBALS['egw_login_data']['passwd']) ||
|
||||
$GLOBALS['egw_login_data']['login'] == 'root_'.$GLOBALS['egw_domain'][$_GET['domain']]['config_user'] &&
|
||||
_check_pw($GLOBALS['egw_domain'][$_GET['domain']]['config_passwd'],$GLOBALS['egw_login_data']['passwd']))
|
||||
{
|
||||
echo "\nRoot access granted!\n";
|
||||
egw_vfs::$is_root = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
die("Unknown user or password!\n");
|
||||
}
|
||||
set_exception_handler('cli_exception_handler'); // otherwise we get html!
|
||||
}
|
||||
}
|
||||
require_once(EGW_API_INC.'/class.'.$scheme.'_stream_wrapper.inc.php');
|
||||
break;
|
||||
case '': // default scheme is file and alsways available
|
||||
@ -250,6 +388,23 @@ function load_wrapper($url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check password against a md5 hash or cleartext password
|
||||
*
|
||||
* @param string $hash_or_cleartext
|
||||
* @param string $pw
|
||||
* @return boolean
|
||||
*/
|
||||
function _check_pw($hash_or_cleartext,$pw)
|
||||
{
|
||||
//echo "_check_pw($hash_or_cleartext,$pw) md5=".md5($pw)."\n";
|
||||
if (preg_match('/^[0-9a-f]{32}$/',$hash_or_cleartext))
|
||||
{
|
||||
return $hash_or_cleartext == md5($pw);
|
||||
}
|
||||
return $hash_or_cleartext == $pw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the stats for one file
|
||||
*
|
||||
@ -257,16 +412,27 @@ function load_wrapper($url)
|
||||
* @param boolean $long=false true=long listing with owner,group,size,perms, default false only filename
|
||||
* @param boolean $numeric=false true=give numeric uid&gid, else resolve the id to a name
|
||||
*/
|
||||
function do_stat($url,$long=false,$numeric=false)
|
||||
function do_stat($url,$long=false,$numeric=false,$full_path=false)
|
||||
{
|
||||
//echo "do_stat($url,$long,$numeric)\n";
|
||||
$bname = basename(parse_url($url,PHP_URL_PATH));
|
||||
//echo "do_stat($url,$long,$numeric,$full_path)\n";
|
||||
$bname = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!$full_path)
|
||||
{
|
||||
$bname = basename($bname);
|
||||
}
|
||||
if ($long && ($stat = stat($url)))
|
||||
{
|
||||
//print_r($stat);
|
||||
//echo $url; print_r($stat);
|
||||
|
||||
$perms = verbosePerms($stat['mode']);
|
||||
if (class_exists('egw_vfs'))
|
||||
{
|
||||
$perms = egw_vfs::int2mode($stat['mode']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$perms = int2mode($stat['mode']);
|
||||
}
|
||||
if ($numeric)
|
||||
{
|
||||
$uid = $stat['uid'];
|
||||
@ -276,14 +442,18 @@ function do_stat($url,$long=false,$numeric=false)
|
||||
{
|
||||
if ($stat['uid'])
|
||||
{
|
||||
$uid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name($stat['uid']) : posix_getpwuid($stat['uid']);
|
||||
$uid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name($stat['uid']) :
|
||||
(function_exists('posix_getpwuid') ? posix_getpwuid($stat['uid']) : $stat['uid']);
|
||||
if (is_array($uid)) $uid = $uid['name'];
|
||||
if (empty($uid)) $uid = $stat['uid'];
|
||||
}
|
||||
if (!isset($uid)) $uid = 'root';
|
||||
if ($stat['gid'])
|
||||
{
|
||||
$gid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name(-abs($stat['gid'])) : posix_getgrgid($stat['gid']);
|
||||
$gid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name(-abs($stat['gid'])) :
|
||||
(function_exists('posix_getgrgid') ? posix_getgrgid($stat['gid']) : $stat['gid']);
|
||||
if (is_array($gid)) $gid = $gid['name'];
|
||||
if (empty($gid)) $gid = $stat['gid'];
|
||||
}
|
||||
if (!isset($gid)) $gid = 'root';
|
||||
}
|
||||
@ -306,33 +476,147 @@ function hsize($size)
|
||||
return sprintf('%3.1lfM',(float)$size/(1024*1024));
|
||||
}
|
||||
|
||||
function verbosePerms( $in_Perms )
|
||||
|
||||
function do_cp($argv,$recursive=false,$perms=false)
|
||||
{
|
||||
if($in_Perms & 0x1000) // FIFO pipe
|
||||
$to = array_pop($argv);
|
||||
load_wrapper($to);
|
||||
|
||||
$to_exists = file_exists($to);
|
||||
|
||||
if (count($argv) > 1 && $to_exists && !is_dir($to))
|
||||
{
|
||||
usage(null,4);
|
||||
}
|
||||
foreach($argv as $from)
|
||||
{
|
||||
if (is_dir($from) && (!file_exists($to) || is_dir($to)) && $recursive && class_exists('egw_vfs'))
|
||||
{
|
||||
foreach(egw_vfs::find($from) as $f)
|
||||
{
|
||||
$t = $to.substr($f,strlen($from));
|
||||
if (is_dir($f))
|
||||
{
|
||||
++$anz_dirs;
|
||||
mkdir($t);
|
||||
}
|
||||
else
|
||||
{
|
||||
++$anz_files;
|
||||
_cp($f,$t);
|
||||
}
|
||||
if ($perms) _cp_perms($f,$t);
|
||||
}
|
||||
echo ($anz_dirs?"$anz_dirs dir(s) created and ":'')."$anz_files file(s) copied.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
_cp($from,$to,true);
|
||||
if ($perms) _cp_perms($from,$to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _cp($from,$to,$verbose=false,$perms=false)
|
||||
{
|
||||
load_wrapper($from);
|
||||
|
||||
if (is_dir($to) || !file_exists($to) && is_dir($from) && $mkdir)
|
||||
{
|
||||
$path = parse_url($from,PHP_URL_PATH);
|
||||
if (is_dir($to)) $to .= '/'.basename($path);
|
||||
}
|
||||
if (!($from_fp = fopen($from,'r')))
|
||||
{
|
||||
die("File $from not found!\n");
|
||||
}
|
||||
if (!($to_fp = fopen($to,'w')))
|
||||
{
|
||||
die("Can't open $to for writing!\n");
|
||||
}
|
||||
$count = stream_copy_to_stream($from_fp,$to_fp);
|
||||
|
||||
if ($verbose) echo hsize($count)." bytes written to $to\n";
|
||||
|
||||
fclose($from_fp);
|
||||
|
||||
if (!fclose($to_fp))
|
||||
{
|
||||
die("Error closing $to!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _cp_perms($from,$to)
|
||||
{
|
||||
if (($from_stat = stat($from)) && ($to_stat = stat($to)))
|
||||
{
|
||||
foreach(array(
|
||||
'mode' => 'chmod',
|
||||
'uid' => 'chown',
|
||||
'gid' => 'chgrp',
|
||||
) as $perm => $cmd)
|
||||
{
|
||||
if ($from_stat[$perm] != $to_stat[$perm])
|
||||
{
|
||||
//echo "egw_vfs::$cmd($to,{$from_stat[$perm]}\n";
|
||||
call_user_func(array('egw_vfs',$cmd),$to,$from_stat[$perm]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function do_find($bases)
|
||||
{
|
||||
foreach($bases as $url)
|
||||
{
|
||||
load_wrapper($url);
|
||||
}
|
||||
foreach(egw_vfs::find($bases) as $path)
|
||||
{
|
||||
echo "$path\n";
|
||||
}
|
||||
}
|
||||
|
||||
function cli_exception_handler(Exception $e)
|
||||
{
|
||||
echo $e->getMessage()."\n";
|
||||
exit($e->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a numerical mode to a symbolic mode-string
|
||||
*
|
||||
* @param int $mode
|
||||
* @return string
|
||||
*/
|
||||
function int2mode( $mode )
|
||||
{
|
||||
if($mode & 0x1000) // FIFO pipe
|
||||
{
|
||||
$sP = 'p';
|
||||
}
|
||||
elseif($in_Perms & 0x2000) // Character special
|
||||
elseif($mode & 0x2000) // Character special
|
||||
{
|
||||
$sP = 'c';
|
||||
}
|
||||
elseif($in_Perms & 0x4000) // Directory
|
||||
elseif($mode & 0x4000) // Directory
|
||||
{
|
||||
$sP = 'd';
|
||||
}
|
||||
elseif($in_Perms & 0x6000) // Block special
|
||||
elseif($mode & 0x6000) // Block special
|
||||
{
|
||||
$sP = 'b';
|
||||
}
|
||||
elseif($in_Perms & 0x8000) // Regular
|
||||
elseif($mode & 0x8000) // Regular
|
||||
{
|
||||
$sP = '-';
|
||||
}
|
||||
elseif($in_Perms & 0xA000) // Symbolic Link
|
||||
elseif($mode & 0xA000) // Symbolic Link
|
||||
{
|
||||
$sP = 'l';
|
||||
}
|
||||
elseif($in_Perms & 0xC000) // Socket
|
||||
elseif($mode & 0xC000) // Socket
|
||||
{
|
||||
$sP = 's';
|
||||
}
|
||||
@ -342,66 +626,22 @@ function verbosePerms( $in_Perms )
|
||||
}
|
||||
|
||||
// owner
|
||||
$sP .= (($in_Perms & 0x0100) ? 'r' : '-') .
|
||||
(($in_Perms & 0x0080) ? 'w' : '-') .
|
||||
(($in_Perms & 0x0040) ? (($in_Perms & 0x0800) ? 's' : 'x' ) :
|
||||
(($in_Perms & 0x0800) ? 'S' : '-'));
|
||||
$sP .= (($mode & 0x0100) ? 'r' : '-') .
|
||||
(($mode & 0x0080) ? 'w' : '-') .
|
||||
(($mode & 0x0040) ? (($mode & 0x0800) ? 's' : 'x' ) :
|
||||
(($mode & 0x0800) ? 'S' : '-'));
|
||||
|
||||
// group
|
||||
$sP .= (($in_Perms & 0x0020) ? 'r' : '-') .
|
||||
(($in_Perms & 0x0010) ? 'w' : '-') .
|
||||
(($in_Perms & 0x0008) ? (($in_Perms & 0x0400) ? 's' : 'x' ) :
|
||||
(($in_Perms & 0x0400) ? 'S' : '-'));
|
||||
$sP .= (($mode & 0x0020) ? 'r' : '-') .
|
||||
(($mode & 0x0010) ? 'w' : '-') .
|
||||
(($mode & 0x0008) ? (($mode & 0x0400) ? 's' : 'x' ) :
|
||||
(($mode & 0x0400) ? 'S' : '-'));
|
||||
|
||||
// world
|
||||
$sP .= (($in_Perms & 0x0004) ? 'r' : '-') .
|
||||
(($in_Perms & 0x0002) ? 'w' : '-') .
|
||||
(($in_Perms & 0x0001) ? (($in_Perms & 0x0200) ? 't' : 'x' ) :
|
||||
(($in_Perms & 0x0200) ? 'T' : '-'));
|
||||
$sP .= (($mode & 0x0004) ? 'r' : '-') .
|
||||
(($mode & 0x0002) ? 'w' : '-') .
|
||||
(($mode & 0x0001) ? (($mode & 0x0200) ? 't' : 'x' ) :
|
||||
(($mode & 0x0200) ? 'T' : '-'));
|
||||
|
||||
return $sP;
|
||||
}
|
||||
|
||||
function do_cp($argv)
|
||||
{
|
||||
$to = array_pop($argv);
|
||||
load_wrapper($to);
|
||||
|
||||
if (count($argv) > 1 && !is_dir($to))
|
||||
{
|
||||
usage(null,4);
|
||||
}
|
||||
if (count($argv) > 1)
|
||||
{
|
||||
foreach($argv as $from)
|
||||
{
|
||||
do_cp(array($from,$to));
|
||||
}
|
||||
return;
|
||||
}
|
||||
$from = array_shift($argv);
|
||||
load_wrapper($from);
|
||||
|
||||
if (!($from_fp = fopen($from,'r')))
|
||||
{
|
||||
die("File $from not found!\n");
|
||||
}
|
||||
if (is_dir($to))
|
||||
{
|
||||
$path = parse_url($from,PHP_URL_PATH);
|
||||
$to .= '/'.basename($path);
|
||||
}
|
||||
if (!($to_fp = fopen($to,'w')))
|
||||
{
|
||||
die("Can't open $to for writing!\n");
|
||||
}
|
||||
$count = stream_copy_to_stream($from_fp,$to_fp);
|
||||
|
||||
echo hsize($count)." bytes written to $to\n";
|
||||
|
||||
fclose($from_fp);
|
||||
|
||||
if (!fclose($to_fp))
|
||||
{
|
||||
die("Error closing $to!\n");
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +59,31 @@
|
||||
*/
|
||||
class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
const EXECUTABLE = 4;
|
||||
const READABLE = 2;
|
||||
const WRITABLE = 1;
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* Current user has root rights, no access checks performed!
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
static $is_root = false;
|
||||
/**
|
||||
* Current user id, in case we ever change if away from $GLOBALS['egw_info']['user']['account_id']
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
static $user;
|
||||
|
||||
/**
|
||||
* fopen working on just the eGW VFS
|
||||
*
|
||||
@ -188,6 +210,131 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* find = recursive search over the filesystem
|
||||
*
|
||||
* @param string/array $base base of the search
|
||||
* @param array $params=null
|
||||
* @param string/array/true $exec=null function to call with each found file or dir as first param or
|
||||
* true to return file => stat pairs
|
||||
* @param array $exec_params=null further params for exec as array, path is always the first param!
|
||||
* @return array of pathes if no $exec, otherwise path => stat pairs
|
||||
*/
|
||||
function find($base,$params=null,$exec=null,$exec_params=null)
|
||||
{
|
||||
//error_log(__METHOD__."(".print_r($base,true).",".print_r($params,true).",".print_r($exec,true).",".print_r($exec_params,true).")\n");
|
||||
|
||||
$type = $params['type']; // 'd' or 'f'
|
||||
$dirs_last = $params['dirs_last']; // list dirs after the files they contain
|
||||
|
||||
if (!is_array($base))
|
||||
{
|
||||
$base = array($base);
|
||||
}
|
||||
foreach($base as $path)
|
||||
{
|
||||
// check our fstab if we need to add some of the mountpoints
|
||||
$basepath = parse_url($path,PHP_URL_PATH);
|
||||
foreach(self::$fstab as $mounted => $src_url)
|
||||
{
|
||||
if (dirname($mounted) == $basepath)
|
||||
{
|
||||
$base[] = $mounted;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = array();
|
||||
foreach($base as $path)
|
||||
{
|
||||
/* if (($scheme = parse_url($path,PHP_URL_SCHEME)))
|
||||
{
|
||||
self::load_wrapper();
|
||||
}*/
|
||||
if (!$type || ($type[0]=='d') == is_dir($path))
|
||||
{
|
||||
if (!($stat = self::url_stat($path,0))) continue;
|
||||
|
||||
if (!$dirs_last || !is_dir($path))
|
||||
{
|
||||
$result[$path] = $stat;
|
||||
}
|
||||
}
|
||||
if (is_dir($path) && ($dir = opendir($path)))
|
||||
{
|
||||
while($file = readdir($dir))
|
||||
{
|
||||
$file = $path.'/'.$file;
|
||||
if (!$type || ($type[0]=='d') == is_dir($file))
|
||||
{
|
||||
if (!($stat = self::url_stat($file,0))) continue;
|
||||
$result[$file] = $stat;
|
||||
}
|
||||
if (is_dir($file))
|
||||
{
|
||||
foreach(self::find($file,$params,true) as $p => $s)
|
||||
{
|
||||
unset($result[$p]);
|
||||
$result[$p] = $s;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
|
||||
if ($dirs_last)
|
||||
{
|
||||
$result[$path] = $stat;
|
||||
}
|
||||
}
|
||||
}
|
||||
//_debug_array($result);
|
||||
if ($exec !== true && is_callable($exec))
|
||||
{
|
||||
if (!is_array($exec_params))
|
||||
{
|
||||
$exec_params = is_null($exec_params) ? array() : array($exec_params);
|
||||
}
|
||||
foreach($result as $path => &$stat)
|
||||
{
|
||||
$params = $exec_params;
|
||||
array_unshift($params,$path);
|
||||
//echo "calling ".print_r($exec,true).print_r($params,true)."\n";
|
||||
$stat = call_user_func_array($exec,$params);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
if ($exec !== true)
|
||||
{
|
||||
return array_keys($result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursiv remove all given url's, including it's content if they are files
|
||||
*
|
||||
* @param string/array $urls url or array of url's
|
||||
* @return array
|
||||
*/
|
||||
static function remove($urls)
|
||||
{
|
||||
return self::find($urls,array('dirs_last'=>true),array(__CLASS__,'_rm_rmdir'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for remove: either rmdir or unlink given url (depending if it's a dir or file)
|
||||
*
|
||||
* @param string $url
|
||||
* @return boolean
|
||||
*/
|
||||
static function _rm_rmdir($url)
|
||||
{
|
||||
if (is_dir($url))
|
||||
{
|
||||
return rmdir($url);
|
||||
}
|
||||
return unlink($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -210,37 +357,42 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
* which is wrong in case of our vfs, as we use the current users id and memberships
|
||||
*
|
||||
* @param array $stat
|
||||
* @param int $check mode to check: 4 = read, 2 = write, 1 = executable
|
||||
* @param int $check mode to check: one or more or'ed together of: 4 = read, 2 = write, 1 = executable
|
||||
* @return boolean
|
||||
*/
|
||||
static function check_access($stat,$check)
|
||||
{
|
||||
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check)");
|
||||
|
||||
if (self::$is_root)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$stat)
|
||||
{
|
||||
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) no stat array!");
|
||||
return false; // file not found
|
||||
}
|
||||
// check if other rights grant access
|
||||
if ($stat['mode'] & $check)
|
||||
if (($stat['mode'] & $check) == $check)
|
||||
{
|
||||
error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via other rights!");
|
||||
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via other rights!");
|
||||
return true;
|
||||
}
|
||||
// check if there's owner access and we are the owner
|
||||
if (($stat['mode'] & ($check << 6)) && $stat['uid'] && $stat['uid'] == $GLOBALS['egw_info']['user']['account_id'])
|
||||
if (($stat['mode'] & ($check << 6)) == ($check << 6) && $stat['uid'] && $stat['uid'] == self::$user)
|
||||
{
|
||||
//error_log(__METHOD__."(stat[name]={$stat['name']},stat[mode]=".sprintf('%o',$stat['mode']).",$check) access via owner rights!");
|
||||
return true;
|
||||
}
|
||||
// check if there's a group access and we have the right membership
|
||||
if (($stat['mode'] & ($check << 3)) && $stat['gid'])
|
||||
if (($stat['mode'] & ($check << 3)) == ($check << 3) && $stat['gid'])
|
||||
{
|
||||
static $memberships;
|
||||
if (is_null($memberships))
|
||||
{
|
||||
$memberships = $GLOBALS['egw']->accounts->memberships($GLOBALS['egw_info']['user']['account_id'],true);
|
||||
$memberships = $GLOBALS['egw']->accounts->memberships(self::$user,true);
|
||||
}
|
||||
if (in_array(-abs($stat['gid']),$memberships))
|
||||
{
|
||||
@ -285,4 +437,131 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a symbolic mode string or octal mode to an integer
|
||||
*
|
||||
* @param string/int $set comma separated mode string to set [ugo]+[+=-]+[rwx]+
|
||||
* @param int $mode=0 current mode of the file, necessary for +/- operation
|
||||
* @return int
|
||||
*/
|
||||
static function mode2int($set,$mode=0)
|
||||
{
|
||||
if (is_int($set)) // already an integer
|
||||
{
|
||||
return $set;
|
||||
}
|
||||
if (is_numeric($set)) // octal string
|
||||
{
|
||||
//error_log(__METHOD__."($set,$mode) returning ".(int)base_convert($set,8,10));
|
||||
return (int)base_convert($set,8,10); // convert octal to decimal
|
||||
}
|
||||
foreach(explode(',',$set) as $s)
|
||||
{
|
||||
if (!preg_match($use='/^([ugoa]*)([+=-]+)([rwx]+)$/',$s,$matches))
|
||||
{
|
||||
$use = str_replace(array('/','^','$','(',')'),'',$use);
|
||||
throw new egw_exception_wrong_userinput("$s is not an allowed mode, use $use !");
|
||||
}
|
||||
$base = (strpos($matches[3],'r') !== false ? self::READABLE : 0) |
|
||||
(strpos($matches[3],'w') !== false ? self::WRITABLE : 0) |
|
||||
(strpos($matches[3],'x') !== false ? self::EXECUTABLE : 0);
|
||||
|
||||
for($n = $m = 0; $n < strlen($matches[1]); $n++)
|
||||
{
|
||||
switch($matches[1][$n])
|
||||
{
|
||||
case 'o':
|
||||
$m |= $base;
|
||||
break;
|
||||
case 'g':
|
||||
$m |= $base << 3;
|
||||
break;
|
||||
case 'u':
|
||||
$m |= $base << 6;
|
||||
break;
|
||||
default:
|
||||
case 'a':
|
||||
$m = $base | ($base << 3) | ($base << 6);
|
||||
}
|
||||
}
|
||||
switch($matches[2])
|
||||
{
|
||||
case '+':
|
||||
$mode |= $m;
|
||||
break;
|
||||
case '=':
|
||||
$mode = $m;
|
||||
break;
|
||||
case '-':
|
||||
$mode &= ~$m;
|
||||
}
|
||||
}
|
||||
//error_log(__METHOD__."($set,) returning ".sprintf('%o',$mode));
|
||||
return $mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a numerical mode to a symbolic mode-string
|
||||
*
|
||||
* @param int $mode
|
||||
* @return string
|
||||
*/
|
||||
static function int2mode( $mode )
|
||||
{
|
||||
if($mode & 0x1000) // FIFO pipe
|
||||
{
|
||||
$sP = 'p';
|
||||
}
|
||||
elseif($mode & 0x2000) // Character special
|
||||
{
|
||||
$sP = 'c';
|
||||
}
|
||||
elseif($mode & 0x4000) // Directory
|
||||
{
|
||||
$sP = 'd';
|
||||
}
|
||||
elseif($mode & 0x6000) // Block special
|
||||
{
|
||||
$sP = 'b';
|
||||
}
|
||||
elseif($mode & 0x8000) // Regular
|
||||
{
|
||||
$sP = '-';
|
||||
}
|
||||
elseif($mode & 0xA000) // Symbolic Link
|
||||
{
|
||||
$sP = 'l';
|
||||
}
|
||||
elseif($mode & 0xC000) // Socket
|
||||
{
|
||||
$sP = 's';
|
||||
}
|
||||
else // UNKNOWN
|
||||
{
|
||||
$sP = 'u';
|
||||
}
|
||||
|
||||
// owner
|
||||
$sP .= (($mode & 0x0100) ? 'r' : '-') .
|
||||
(($mode & 0x0080) ? 'w' : '-') .
|
||||
(($mode & 0x0040) ? (($mode & 0x0800) ? 's' : 'x' ) :
|
||||
(($mode & 0x0800) ? 'S' : '-'));
|
||||
|
||||
// group
|
||||
$sP .= (($mode & 0x0020) ? 'r' : '-') .
|
||||
(($mode & 0x0010) ? 'w' : '-') .
|
||||
(($mode & 0x0008) ? (($mode & 0x0400) ? 's' : 'x' ) :
|
||||
(($mode & 0x0400) ? 'S' : '-'));
|
||||
|
||||
// world
|
||||
$sP .= (($mode & 0x0004) ? 'r' : '-') .
|
||||
(($mode & 0x0002) ? 'w' : '-') .
|
||||
(($mode & 0x0001) ? (($mode & 0x0200) ? 't' : 'x' ) :
|
||||
(($mode & 0x0200) ? 'T' : '-'));
|
||||
|
||||
return $sP;
|
||||
}
|
||||
}
|
||||
|
||||
egw_vfs::$user = (int) $GLOBALS['egw_info']['user']['account_id'];
|
||||
|
@ -31,10 +31,6 @@
|
||||
*/
|
||||
class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
/**
|
||||
* If this class should do the operations direct in the filesystem, instead of going through the vfs
|
||||
*/
|
||||
const USE_FILESYSTEM_DIRECT = true;
|
||||
/**
|
||||
* Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory'
|
||||
*/
|
||||
@ -193,12 +189,15 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
$values = array(
|
||||
'fs_name' => basename($path),
|
||||
'fs_dir' => $dir_stat['ino'],
|
||||
// we use the mode of the dir, so files in group dirs stay accessible by all members
|
||||
'fs_mode' => $dir_stat['mode'] & 0666,
|
||||
'fs_uid' => $dir_stat['uid'],
|
||||
// for the uid we use the uid of the dir if not 0=root or the current user otherwise
|
||||
'fs_uid' => $dir_stat['uid'] ? $dir_stat['uid'] : egw_vfs::$user,
|
||||
// we allways use the group of the dir
|
||||
'fs_gid' => $dir_stat['gid'],
|
||||
'fs_created' => self::_pdo_timestamp(time()),
|
||||
'fs_modified' => self::_pdo_timestamp(time()),
|
||||
'fs_creator' => $GLOBALS['egw_info']['user']['account_id'],
|
||||
'fs_creator' => egw_vfs::$user,
|
||||
);
|
||||
foreach($values as $name => &$val)
|
||||
{
|
||||
@ -530,7 +529,18 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
$parent = self::url_stat(dirname($path),0);
|
||||
if (self::url_stat($path,STREAM_URL_STAT_QUIET))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) already exist!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
{
|
||||
trigger_error(__METHOD__."('$url',$mode,$options) already exist!",E_USER_WARNING);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$parent = self::url_stat(dirname($path),STREAM_URL_STAT_QUIET);
|
||||
|
||||
// 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
|
||||
@ -545,7 +555,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
if (!$parent || !egw_vfs::check_access($parent,egw_vfs::WRITABLE))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) permission denied!");
|
||||
if (!($options & STREAM_URL_STAT_QUIET))
|
||||
{
|
||||
trigger_error(__METHOD__."('$url',$mode,$options) permission denied!",E_USER_WARNING);
|
||||
@ -564,7 +574,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
':fs_mime' => self::DIR_MIME_TYPE,
|
||||
':fs_created' => self::_pdo_timestamp(time()),
|
||||
':fs_modified' => self::_pdo_timestamp(time()),
|
||||
':fs_creator' => $GLOBALS['egw_info']['user']['account_id'],
|
||||
':fs_creator' => egw_vfs::$user,
|
||||
))) && $operation == self::STORE2FS)
|
||||
{
|
||||
mkdir(self::_fs_path($path));
|
||||
@ -634,10 +644,21 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!($stat = self::url_stat($path,0)))
|
||||
if (!($stat = self::url_stat($path,STREAM_URL_STAT_QUIET)))
|
||||
{
|
||||
// file does not exist --> create an empty one
|
||||
if (($f = fopen(self::SCHEME.'://default'.$path,'w')) && fclose($f))
|
||||
{
|
||||
if (!is_null($time))
|
||||
{
|
||||
$stat = self::url_stat($path,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_modified=:fs_modified WHERE fs_id=:fs_id');
|
||||
|
||||
return $stmt->execute(array(
|
||||
@ -646,9 +667,130 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chown command, not yet a stream-wrapper function, but necessary
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $owner
|
||||
* @return boolean
|
||||
*/
|
||||
static function chown($url,$owner)
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!($stat = self::url_stat($path,0)))
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if (!egw_vfs::$is_root)
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only root can do that!");
|
||||
trigger_error("Only root can do that!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
if ($owner < 0 || !$GLOBALS['egw']->accounts->id2name($owner)) // not a user
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) unknown (numeric) user id!");
|
||||
trigger_error("Unknown (numeric) user id!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_uid=:fs_uid WHERE fs_id=:fs_id');
|
||||
|
||||
return $stmt->execute(array(
|
||||
':fs_uid' => (int) $owner,
|
||||
':fs_id' => $stat['ino'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chgrp command, not yet a stream-wrapper function, but necessary
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $group
|
||||
* @return boolean
|
||||
*/
|
||||
static function chgrp($url,$owner)
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!($stat = self::url_stat($path,0)))
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if (!egw_vfs::$is_root && $stat['uid'] != egw_vfs::$user)
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only owner or root can do that!");
|
||||
trigger_error("Only owner or root can do that!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
if ($owner < 0) $owner = -$owner; // sqlfs uses a positiv group id's!
|
||||
|
||||
if ($owner && !$GLOBALS['egw']->accounts->id2name(-$owner)) // not a group
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) unknown (numeric) group id!");
|
||||
trigger_error("Unknown (numeric) group id!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_gid=:fs_gid WHERE fs_id=:fs_id');
|
||||
|
||||
return $stmt->execute(array(
|
||||
':fs_gid' => $owner,
|
||||
':fs_id' => $stat['ino'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chmod command, not yet a stream-wrapper function, but necessary
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $mode
|
||||
* @return boolean
|
||||
*/
|
||||
static function chmod($url,$mode)
|
||||
{
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");
|
||||
|
||||
$path = parse_url($url,PHP_URL_PATH);
|
||||
|
||||
if (!($stat = self::url_stat($path,0)))
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
if (!egw_vfs::$is_root && $stat['uid'] != egw_vfs::$user)
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only owner or root can do that!");
|
||||
trigger_error("Only owner or root can do that!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
if (!is_numeric($mode)) // not a mode
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no (numeric) mode!");
|
||||
trigger_error("No (numeric) mode!",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_mode=:fs_mode WHERE fs_id=:fs_id');
|
||||
|
||||
return $stmt->execute(array(
|
||||
':fs_mode' => ((int) $mode) & 0777, // we dont store the file and dir bits, give int overflow!
|
||||
':fs_id' => $stat['ino'],
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is called immediately when your stream object is created for examining directory contents with opendir().
|
||||
*
|
||||
* @ToDo check all parent dirs for readable (fastest would be with sql query) !!!
|
||||
* @param string $path URL that was passed to opendir() and that this object is expected to explore.
|
||||
* @param $options
|
||||
* @return booelan
|
||||
@ -663,7 +805,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
|
||||
if (!($stat = self::url_stat($url,0)) || // dir not found
|
||||
$stat['mime'] != self::DIR_MIME_TYPE || // no dir
|
||||
!egw_vfs::check_access($stat,egw_vfs::EXECUTABLE)) // no access
|
||||
!egw_vfs::check_access($stat,egw_vfs::EXECUTABLE|egw_vfs::READABLE)) // no access
|
||||
{
|
||||
self::_remove_password($url);
|
||||
$msg = $stat['mime'] != self::DIR_MIME_TYPE ? "$url is no directory" : 'permission denied';
|
||||
@ -672,7 +814,13 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
return false;
|
||||
}
|
||||
self::$stat_cache = $this->opened_dir = array();
|
||||
$stmt = self::$pdo->prepare('SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified FROM '.self::TABLE.' WHERE fs_dir=?');
|
||||
$query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified FROM '.self::TABLE.' WHERE fs_dir=?';
|
||||
// only return readable files, if dir is not writable by user
|
||||
if (!egw_vfs::check_access($stat,egw_vfs::WRITABLE))
|
||||
{
|
||||
$query .= ' AND '.self::_sql_readable();
|
||||
}
|
||||
$stmt = self::$pdo->prepare($query);
|
||||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||||
if ($stmt->execute(array($stat['ino'])))
|
||||
{
|
||||
@ -741,11 +889,17 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
if ($n == 0)
|
||||
{
|
||||
$query = 1; // / always has fs_id == 1, no need to query it
|
||||
$query = (int) ($path != '/'); // / always has fs_id == 1, no need to query it ($path=='/' needs fs_dir=0!)
|
||||
}
|
||||
elseif ($n < count($parts)-1)
|
||||
{
|
||||
$query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_name='.self::$pdo->quote($name);
|
||||
|
||||
// if we are not root, we need to make sure the user has the right to tranverse all partent directories (read-rights)
|
||||
if (!egw_vfs::$is_root)
|
||||
{
|
||||
$query .= ' AND '.$sql_read_acl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -757,7 +911,7 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
if (!($result = self::$pdo->query($query)) || !($info = $result->fetch(PDO::FETCH_ASSOC)))
|
||||
{
|
||||
self::_remove_password($url);
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$flags) file or directory not found!");
|
||||
if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags) file or directory not found!");
|
||||
return false;
|
||||
}
|
||||
self::$stat_cache[$path] = $info;
|
||||
@ -765,6 +919,27 @@ class sqlfs_stream_wrapper implements iface_stream_wrapper
|
||||
return self::_vfsinfo2stat($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return readable check as sql (to be AND'ed into the query), only use if !egw_vfs::$is_root
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function _sql_readable()
|
||||
{
|
||||
static $sql_read_acl;
|
||||
|
||||
if (is_null($sql_read_acl))
|
||||
{
|
||||
foreach($GLOBALS['egw']->accounts->memberships(egw_vfs::$user,true) as $gid)
|
||||
{
|
||||
$memberships[] = abs($gid); // sqlfs stores the gid's positiv
|
||||
}
|
||||
$sql_read_acl = '(fs_mode & 04 OR fs_mode & 0400 AND fs_uid='.(int)egw_vfs::$user.
|
||||
' OR fs_mode & 040 AND fs_gid IN('.implode(',',$memberships).'))';
|
||||
}
|
||||
return $sql_read_acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called in response to readdir().
|
||||
*
|
||||
|
@ -63,6 +63,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
* @var ressource
|
||||
*/
|
||||
private $opened_dir;
|
||||
/**
|
||||
* URL of the opened dir, used to build the complete URL of files in the dir
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $opened_dir_url;
|
||||
/**
|
||||
* Flag if opened dir is writable, in which case we return un-readable entries too
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $opened_dir_writable;
|
||||
/**
|
||||
* Extra dirs from our fstab in the current opened dir
|
||||
*
|
||||
@ -357,28 +369,93 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not (yet) a stream-wrapper function, but it's necessary and can be used static
|
||||
* Allow to call methods of the underlying stream wrapper: touch, chmod, chgrp, chown, ...
|
||||
*
|
||||
* @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!
|
||||
* 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
|
||||
*/
|
||||
static function touch($path,$time=null,$atime=null)
|
||||
static private function _call_on_backend($name,$params)
|
||||
{
|
||||
if (!($url = self::resolve_url($path)))
|
||||
$path = $params[0];
|
||||
|
||||
if (!($url = self::resolve_url($params[0])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (($scheme = parse_url($url,PHP_URL_SCHEME)))
|
||||
{
|
||||
if (!class_exists($class = $scheme.'_stream_wrapper') || !method_exists($class,'touch'))
|
||||
if (!class_exists($class = $scheme.'_stream_wrapper') || !method_exists($class,$name))
|
||||
{
|
||||
trigger_error("Can't touch for scheme $scheme!\n",E_USER_WARNING);
|
||||
trigger_error("Can't $name for scheme $scheme!\n",E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
return call_user_func(array($scheme.'_stream_wrapper','touch'),$url,$time);
|
||||
$params[0] = $url;
|
||||
|
||||
return call_user_func_array(array($scheme.'_stream_wrapper',$name),$params);
|
||||
}
|
||||
return touch($url,$time);
|
||||
// call the filesystem specific function
|
||||
if (!function_exists($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $name($url,$time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 egw_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));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -446,16 +523,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
$this->opened_dir = $this->extra_dirs = null;
|
||||
$this->extra_dir_ptr = 0;
|
||||
|
||||
if (!($url = self::resolve_url($path)))
|
||||
if (!($this->opened_dir_url = self::resolve_url($path)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!($this->opened_dir = opendir($url)))
|
||||
if (!($this->opened_dir = opendir($this->opened_dir_url)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$this->opened_dir_writable = ($stat = @stat($this->opened_dir_url)) && egw_vfs::check_access($stat,egw_vfs::WRITABLE);
|
||||
|
||||
// check our fstab if we need to add some of the mountpoints
|
||||
$basepath = parse_url($url,PHP_URL_PATH);
|
||||
$basepath = parse_url($this->opened_dir_url,PHP_URL_PATH);
|
||||
foreach(self::$fstab as $mounted => $nul)
|
||||
{
|
||||
if (dirname($mounted) == $basepath)
|
||||
@ -500,8 +579,8 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
error_log(__METHOD__."('$path',$flags) calling stat($url)");
|
||||
return stat($url);
|
||||
//error_log(__METHOD__."('$path',$flags) calling stat($url)");
|
||||
return @stat($url); // suppressed the stat failed warnings
|
||||
}
|
||||
|
||||
/**
|
||||
@ -509,6 +588,9 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
*
|
||||
* It should return a string representing the next filename in the location opened by dir_opendir().
|
||||
*
|
||||
* Unless other filesystem, we only return files readable by the user, if the dir is not writable for him.
|
||||
* This is done to hide files and dirs not accessible by the user (eg. other peoples home-dirs in /home).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function dir_readdir ( )
|
||||
@ -517,7 +599,18 @@ class vfs_stream_wrapper implements iface_stream_wrapper
|
||||
{
|
||||
return $this->extra_dirs[$this->extra_dir_ptr++];
|
||||
}
|
||||
return readdir($this->opened_dir);
|
||||
// only return children readable by the user, if dir is not writable
|
||||
do {
|
||||
if (($file = readdir($this->opened_dir)) !== false && !$this->opened_dir_writable)
|
||||
{
|
||||
$stat = stat($this->opened_dir_url.'/'.$file);
|
||||
}
|
||||
//echo __METHOD__."() opened_dir_writable=$this->opened_dir_writable, file=$file, readable=".(int)egw_vfs::check_access($stat,egw_vfs::READABLE).", loop=".
|
||||
// (int)($file !== false && $stat && !egw_vfs::check_access($stat,egw_vfs::READABLE))."\n";
|
||||
}
|
||||
while($file !== false && $stat && !egw_vfs::check_access($stat,egw_vfs::READABLE));
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user