diff --git a/filemanager/ls.php b/filemanager/ls.php index dae7e1892b..fb7ab4eea9 100755 --- a/filemanager/ls.php +++ b/filemanager/ls.php @@ -18,25 +18,6 @@ if (isset($_SERVER['HTTP_HOST'])) // security precaution: forbit calling ls as w die('

'.basename(__FILE__).' must NOT be called as web-page --> exiting !!!

'); } -/* -// this is kind of a hack, as the autocreate_session_callback can not change the type of the loaded account-class -// so we need to make sure the right one is loaded by setting the domain before the header gets included. -$arg0s = explode(',',@$arguments[0]); -@list(,$_GET['domain']) = explode('@',$arg0s[0]); - -if (is_dir('/tmp')) ini_set('session.save_path','/tmp'); // regular users may have no rights to apache's session dir - -$GLOBALS['egw_info'] = array( - 'flags' => array( - 'currentapp' => 'admin', - 'noheader' => true, - 'autocreate_session_callback' => 'user_pass_from_argv', - ) -); - -include('../header.inc.php'); -*/ - /** * callback if the session-check fails, redirects via xajax to login.php * @@ -64,14 +45,19 @@ function user_pass_from_argv(&$account) function usage($action=null,$ret=0) { $cmd = basename(__FILE__); - echo "Usage: $cmd URL\n"; - echo "\t$cmd --cat URL\n"; + echo "Usage:\t$cmd URL [URL2 ...]\n"; + echo "\t$cmd --cat URL [URL2 ...]\n"; echo "\t$cmd --cp URL-from URL-to\n"; - echo "\t$cmd --cp URL-from1 [URL-from2 ...] URL-to-directory\n"; + echo "\t$cmd --cp URL-from [URL-from2 ...] URL-to-directory\n"; + echo "\t$cmd --rm 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"; exit; } -$long = $numeric = false; +$long = $numeric = $recursive = false; $argv = $_SERVER['argv']; $cmd = basename(array_shift($argv),'.php'); @@ -97,9 +83,24 @@ foreach($argv as $key => $option) $numeric = true; continue 2; // switch is counting too! + case '-r': case '--recursive': + case '-p': case '--parents': + $recursive = true; + continue 2; // switch is counting too! + + case '-d': case '--date': + $time = strtotime($argv[$key+1]); + unset($argv[$key+1]); + break; + case '--cat': // cat files (!) to stdout - case '--cp': // cp files - case '--rm': // rm files + case '--cp': // copy files + case '--rm': // remove files + case '--ls': // list files + case '--rmdir': // remove dirs + case '--mkdir': // make directories + case '--rename':// rename + case '--touch': // touch $cmd = substr($option,2); continue 2; // switch is counting too! } @@ -113,49 +114,90 @@ switch($cmd) do_cp($argv); break; + case 'rename': + if (count($argv) != 2) usage(null,3); + load_wrapper($argv[0]); + load_wrapper($argv[1]); + rename($argv[0],$argv[1]); + break; + default: while($url = array_shift($argv)) { load_wrapper($url); //echo "$cmd $url (long=".(int)$long.", numeric=".(int)$numeric.")\n"; - if ($cmd == 'rm') + switch($cmd) { - unlink($url); - } - elseif (is_dir($url) && ($dir = opendir($url))) - { - if ($argc) - { - echo "\n".basename(parse_url($url,PHP_URL_PATH)).":\n"; - } - while(($file = readdir($dir)) !== false) - { - do_stat($url.'/'.$file,$long,$numeric); - } - closedir($dir); - } - elseif ($cmd == 'cat') - { - if (!($f = fopen($url,'r'))) - { - echo "File $url not found !!!\n\n"; - } - else - { - if ($argc) + case 'rm': + unlink($url); + break; + + case 'rmdir': + rmdir($url); + break; + + case 'mkdir': + mkdir($url,null,$recursive); + break; + + case 'touch': + if (($scheme = parse_url($url,PHP_URL_SCHEME))) { - echo "\n".basename(parse_url($url,PHP_URL_PATH)).":\n"; + load_wrapper($url); + if (class_exists($class = $scheme.'_stream_wrapper') && method_exists($class,'touch')) + { + call_user_func(array($scheme.'_stream_wrapper','touch'),$url,$time); + } + else + { + die("Can't touch for scheme $scheme!\n"); + } } - fpassthru($f); - fclose($f); - } + else + { + touch($url,$time); + } + break; + + case 'cat': + case 'ls': + default: + if (is_dir($url) && ($dir = opendir($url))) + { + if ($argc) + { + echo "\n".basename(parse_url($url,PHP_URL_PATH)).":\n"; + } + while(($file = readdir($dir)) !== false) + { + do_stat($url.'/'.$file,$long,$numeric); + } + closedir($dir); + } + elseif ($cmd == 'cat') + { + if (!($f = fopen($url,'r'))) + { + echo "File $url not found !!!\n\n"; + } + else + { + if ($argc) + { + echo "\n".basename(parse_url($url,PHP_URL_PATH)).":\n"; + } + fpassthru($f); + fclose($f); + } + } + else + { + do_stat($url,$long,$numeric); + } + if (!$long && $cmd == 'ls') echo "\n"; + break; } - else - { - do_stat($url,$long,$numeric); - } - if (!$long && $cmd == 'ls') echo "\n"; } } @@ -166,12 +208,13 @@ switch($cmd) */ function load_wrapper($url) { - switch(parse_url($url,PHP_URL_SCHEME)) + switch($scheme = parse_url($url,PHP_URL_SCHEME)) { case 'webdav': require_once('HTTP/WebDAV/Client.php'); break; case 'oldvfs': + case 'vfs': if (!isset($GLOBALS['egw_info'])) { $_GET['domain'] = parse_url($url,PHP_URL_HOST); @@ -193,13 +236,16 @@ function load_wrapper($url) include('../header.inc.php'); } - require_once(EGW_API_INC.'/class.oldvfs_stream_wrapper.inc.php'); + require_once(EGW_API_INC.'/class.'.$scheme.'_stream_wrapper.inc.php'); break; - case '': - case 'ftp': + case '': // default scheme is file and alsways available break; default: - die("Unknown scheme in $url !!!\n\n"); + if (!in_array($scheme,stream_get_wrappers())) + { + die("Unknown scheme '$scheme' in $url !!!\n\n"); + } + break; } } @@ -215,9 +261,8 @@ function do_stat($url,$long=false,$numeric=false) //echo "do_stat($url,$long,$numeric)\n"; $bname = basename(parse_url($url,PHP_URL_PATH)); - if ($long) + if ($long && ($stat = stat($url))) { - $stat = stat($url); //print_r($stat); $perms = verbosePerms($stat['mode']); @@ -233,13 +278,13 @@ function do_stat($url,$long=false,$numeric=false) $uid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name($stat['uid']) : posix_getpwuid($stat['uid']); if (is_array($uid)) $uid = $uid['name']; } - if (!isset($uid)) $uid = 'none'; + if (!isset($uid)) $uid = 'root'; if ($stat['gid']) { $gid = isset($GLOBALS['egw']) ? $GLOBALS['egw']->accounts->id2name($stat['gid']) : posix_getgrgid($stat['gid']); if (is_array($gid)) $gid = $gid['name']; } - if (!isset($gid)) $gid = 'none'; + if (!isset($gid)) $gid = 'root'; } $size = hsize($stat['size']); $mtime = date('Y-m-d H:i:s',$stat['mtime']); @@ -322,7 +367,7 @@ function do_cp($argv) if (count($argv) > 1 && !is_dir($to)) { - die ("Usage: cp from-file to-file | cp file1 [file2 ...] dir\n\n"); + usage(null,4); } if (count($argv) > 1) { @@ -337,7 +382,7 @@ function do_cp($argv) if (!($from_fp = fopen($from,'r'))) { - die("File $from not found!"); + die("File $from not found!\n"); } if (is_dir($to)) { @@ -346,7 +391,7 @@ function do_cp($argv) } if (!($to_fp = fopen($to,'w'))) { - die("Can't open $to from writing!"); + die("Can't open $to for writing!\n"); } $count = stream_copy_to_stream($from_fp,$to_fp); diff --git a/phpgwapi/inc/class.iface_stream_wrapper.inc.php b/phpgwapi/inc/class.iface_stream_wrapper.inc.php index 5214db5d99..48da3c762a 100644 --- a/phpgwapi/inc/class.iface_stream_wrapper.inc.php +++ b/phpgwapi/inc/class.iface_stream_wrapper.inc.php @@ -14,7 +14,7 @@ * * The interface is according to the docu on php.net * - * @link http://de.php.net/manual/de/function.stream-wrapper-register.php + * @link http://www.php.net/manual/en/function.stream-wrapper-register.php */ interface iface_stream_wrapper { @@ -149,11 +149,13 @@ interface iface_stream_wrapper * It should attempt to rename the item specified by path_from to the specification given by path_to. * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support renaming. * + * The regular filesystem stream-wrapper returns an error, if $url_from and $url_to are not either both files or both dirs! + * * @param string $path_from * @param string $path_to * @return boolean TRUE on success or FALSE on failure */ - function rename ( $path_from, $path_to ); + static function rename ( $path_from, $path_to ); /** * This method is called in response to mkdir() calls on URL paths associated with the wrapper. @@ -166,7 +168,7 @@ interface iface_stream_wrapper * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE * @return boolean TRUE on success or FALSE on failure */ - function mkdir ( $path, $mode, $options ); + static function mkdir ( $path, $mode, $options ); /** * This method is called in response to rmdir() calls on URL paths associated with the wrapper. @@ -178,7 +180,7 @@ interface iface_stream_wrapper * @param int $options Possible values include STREAM_REPORT_ERRORS. * @return boolean TRUE on success or FALSE on failure. */ - function rmdir ( $path, $options ); + static function rmdir ( $path, $options ); /** * This method is called immediately when your stream object is created for examining directory contents with opendir(). @@ -211,9 +213,10 @@ interface iface_stream_wrapper * This flag is set in response to calls to lstat(), is_link(), or filetype(). * - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set, * you are responsible for reporting errors using the trigger_error() function during stating of the path. + * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! * @return array */ - function url_stat ( $path, $flags ); + static function url_stat ( $path, $flags ); /** * This method is called in response to readdir(). diff --git a/phpgwapi/inc/class.oldvfs_stream_wrapper.inc.php b/phpgwapi/inc/class.oldvfs_stream_wrapper.inc.php index 7f344af441..1adb2f3ae8 100644 --- a/phpgwapi/inc/class.oldvfs_stream_wrapper.inc.php +++ b/phpgwapi/inc/class.oldvfs_stream_wrapper.inc.php @@ -7,7 +7,7 @@ * @package api * @subpackage vfs * @author Ralf Becker - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-8 by Ralf Becker * @version $Id$ */ @@ -30,12 +30,17 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper * */ const USE_FILESYSTEM_DIRECT = true; + /** + * Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory' + * + */ + const DIR_MIME_TYPE = 'Directory'; /** * How much should be logged to the apache error-log * * 0 = Nothing * 1 = only errors - * 2 = all function calls and errors + * 2 = all function calls and errors (contains passwords too!) */ const LOG_LEVEL = 1; @@ -78,20 +83,22 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper */ protected $opened_pos; /** - * Global vfs::ls() cache + * Directory vfs::ls() of dir opened with dir_opendir() + * + * This static var gets overwritten by each new dir_opendir, it helps to not read the entries twice. + * + * @var array $path => info-array pairs + */ + static private $stat_cache; + /** + * Array with filenames of dir opened with dir_opendir * * @var array */ - static protected $cache=array(); - /** - * Directory vfs::ls() of path opened with dir_opendir() - * - * @var string - */ protected $opened_dir; /** - * Constructor + * Constructor, only called for the non-static stream_* methods! * * @return oldvfs_stream_wrapper */ @@ -138,7 +145,12 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper 'operation' => EGW_ACL_ADD, ))) { + self::remove_password($url); if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file does not exist or can not be created!"); + if (!($options & STREAM_URL_STAT_QUIET)) + { + trigger_error(__METHOD__."($url,$mode,$options) file does not exist or can not be created!",E_USER_WARNING); + } $this->opened_data = $this->opened_path = $this->opened_mode = null; return false; } @@ -152,7 +164,12 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper 'operation' => EGW_ACL_EDIT, ))) { + self::remove_password($url); if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file can not be edited!"); + if (!($options & STREAM_URL_STAT_QUIET)) + { + trigger_error(__METHOD__."($url,$mode,$options) file can not be edited!",E_USER_WARNING); + } $this->opened_data = $this->opened_path = $this->opened_mode = null; return false; } @@ -311,8 +328,10 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper $len = bytes($this->opened_data); $eof = $this->opened_pos >= $len; } - if (self::LOG_LEVEL > 1) error_log(__METHOD__."() pos=$this->opened_pos >= $len=len --> ".($eof ? 'true' : 'false')); - + if (self::LOG_LEVEL > 1) + { + error_log(__METHOD__."() pos=$this->opened_pos >= $len=len --> ".($eof ? 'true' : 'false')); + } return $eof; } @@ -385,6 +404,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper { return fflush($this->opened_data); } + // we cant flush, as the old vfs can only write the whole data as once return true; } @@ -415,13 +435,15 @@ class oldvfs_stream_wrapper implements iface_stream_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 + * @param string $url * @return boolean TRUE on success or FALSE on failure */ - static function unlink ( $path ) + static function unlink ( $url ) { - if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path)"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); + $path = parse_url($url,PHP_URL_PATH); + if (!is_object(self::$old_vfs)) { self::$old_vfs =& new vfs_home(); @@ -431,12 +453,16 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root ); if (!self::$old_vfs->acl_check($data+array( - 'operation' => EGW_ACL_DELETE - )) /*|| ($type = self::$old_vfs->file_type($data)) === 'Directory'*/) + 'operation' => EGW_ACL_DELETE, + 'must_exist' => true, + )) || ($type = self::$old_vfs->file_type($data)) === self::DIR_MIME_TYPE) { - if (self::LOG_LEVEL) error_log(__METHOD__."($path) (type=$type) permission denied!"); + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."($url) (type=$type) permission denied!"); return false; // no permission or file does not exist } + unset(self::$stat_cache[$path]); + return self::$old_vfs->rm($data); } @@ -445,15 +471,67 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper * * It should attempt to rename the item specified by path_from to the specification given by path_to. * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support renaming. + * + * The regular filesystem stream-wrapper returns an error, if $url_from and $url_to are not either both files or both dirs! * - * @param string $path_from - * @param string $path_to + * @param string $url_from + * @param string $url_to * @return boolean TRUE on success or FALSE on failure */ - function rename ( $path_from, $path_to ) + static function rename ( $url_from, $url_to ) { - error_log(__METHOD__."($path_from, $path_to) not yet implemented!"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url_from,$url_to)"); + $path_from = parse_url($url_from,PHP_URL_PATH); + $path_to = parse_url($url_to,PHP_URL_PATH); + + if (!is_object(self::$old_vfs)) + { + self::$old_vfs =& new vfs_home(); + } + $data_from = array( + 'string' => $path_from, + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ); + if (!self::$old_vfs->acl_check($data_from+array( + 'operation' => EGW_ACL_DELETE, + 'must_exist'=> true, + ))) + { + if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_from permission denied!"); + return false; // no permission or file does not exist + } + $data_to = array( + 'string' => $path_to, + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ); + if (!self::$old_vfs->acl_check($data_to+array( + 'operation' => EGW_ACL_ADD, + ))) + { + self::remove_password($url_from); + self::remove_password($url_to); + if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!"); + return false; // no permission or file does not exist + } + // the filesystem stream-wrapper does NOT allow to rename files to directories, as this makes problems + // for our vfs too, we give abort here with an error, like the filesystem one does + if (($type_to = self::$old_vfs->file_type($data_to)) && + ($type_to === self::DIR_MIME_TYPE) !== (self::$old_vfs->file_type($data_from) === self::DIR_MIME_TYPE)) + { + $is_dir = $type_to === self::DIR_MIME_TYPE ? 'a' : 'no'; + self::remove_password($url_from); + self::remove_password($url_to); + if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $path_to is $is_dir directory!"); + return false; // no permission or file does not exist + } + unset(self::$stat_cache[$path_from]); + + return self::$old_vfs->mv(array( + 'from' => $path_from, + 'to' => $path_to, + 'relatives' => array(RELATIVE_ROOT,RELATIVE_ROOT), + )); } /** @@ -462,15 +540,51 @@ class oldvfs_stream_wrapper implements iface_stream_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 string $url * @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 */ - function mkdir ( $path, $mode, $options ) + static function mkdir ( $url, $mode, $options ) { - error_log(__METHOD__."($path, $mode, $options) not yet implemented!"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)"); + $path = parse_url($url,PHP_URL_PATH); + + if (!is_object(self::$old_vfs)) + { + self::$old_vfs =& new vfs_home(); + } + // 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 (($options & STREAM_MKDIR_RECURSIVE) && $path != '/' && !self::$old_vfs->file_exists(array( + 'string' => dirname($path), + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ))) + { + if (!self::mkdir(dirname($path),$mode,$options)) + { + return false; + } + } + $data=array( + 'string' => $path, + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ); + if (!self::$old_vfs->acl_check($data+array( + 'operation' => EGW_ACL_ADD, + 'must_exist' => false, + ))) + { + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!"); + if (!($options & STREAM_URL_STAT_QUIET)) + { + trigger_error(__METHOD__."('$url',$mode,$options) permission denied!",E_USER_WARNING); + } + return false; // no permission or file does not exist + } + return self::$old_vfs->mkdir($data); } /** @@ -479,16 +593,80 @@ class oldvfs_stream_wrapper implements iface_stream_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 string $url * @param int $options Possible values include STREAM_REPORT_ERRORS. * @return boolean TRUE on success or FALSE on failure. */ - function rmdir ( $path, $options ) + static function rmdir ( $url, $options ) { - error_log(__METHOD__."($path, $options) not yet implemented!"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); + $path = parse_url($url,PHP_URL_PATH); + + if (!is_object(self::$old_vfs)) + { + self::$old_vfs =& new vfs_home(); + } + $data=array( + 'string' => $path, + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ); + if (!self::$old_vfs->acl_check($data+array( + 'operation' => EGW_ACL_DELETE, + 'must_exist' => true, + )) || ($type = self::$old_vfs->file_type($data)) !== self::DIR_MIME_TYPE) + { + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) (type=$type) permission denied!"); + if (!($options & STREAM_URL_STAT_QUIET)) + { + trigger_error(__METHOD__."('$url',$options) (type=$type) permission denied!",E_USER_WARNING); + } + return false; // no permission or file does not exist + } + // abort with an error, if the dir is not empty + // our vfs deletes recursive, while the stream-wrapper interface does not! + if (($files = self::$old_vfs->ls($data))) + { + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) dir is not empty!"); + if (!($options & STREAM_URL_STAT_QUIET)) + { + trigger_error(__METHOD__."('$url',$options) dir is not empty!",E_USER_WARNING); + } + return false; + } + unset(self::$stat_cache[$path]); + + return self::$old_vfs->rm($data); } + /** + * This is not (yet) a stream-wrapper function, but it's necessary and can be used static + * + * @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! + */ + static function touch($url,$time=null,$atime=null) + { + if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)"); + + $path = parse_url($url,PHP_URL_PATH); + + if (!is_object(self::$old_vfs)) + { + self::$old_vfs =& new vfs_home(); + } + $data=array( + 'string' => $path, + 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root + ); + if (!is_null($time)) $data['time'] = $time; + + return self::$old_vfs->touch($data); + } + /** * This method is called immediately when your stream object is created for examining directory contents with opendir(). * @@ -499,6 +677,8 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper function dir_opendir ( $url, $options ) { if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$options)"); + + $this->opened_dir = null; if (!is_object(self::$old_vfs)) { @@ -506,22 +686,30 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper } $path = parse_url($url,PHP_URL_PATH); - $this->opened_dir = self::$old_vfs->ls(array( + $files = self::$old_vfs->ls(array( 'string' => $path, 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root 'checksubdirs' => false, 'nofiles' => false, - //'orderby' => '', + 'orderby' => 'name', //'mime_type' => '', )); - if (!is_array($this->opened_dir) || + if (!is_array($files) || // we also need to return false, if $url is not a directory! - count($this->opened_dir) == 1 && $path == $this->opened_dir[0]['directory'].'/'.$this->opened_dir[0]['name'] && - $this->opened_dir[0]['mime_type'] != 'Directory') + count($files) == 1 && $path == $files[0]['directory'].'/'.$files[0]['name'] && + $files[0]['mime_type'] != self::DIR_MIME_TYPE) { + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$options) $url is not directory!"); $this->opened_dir = null; return false; } + self::$stat_cache = $this->opened_dir = array(); + foreach($files as $file) + { + $this->opened_dir[] = $file['name']; + self::$stat_cache[$file['directory'].'/'.$file['name']] = $file; + } //print_r($this->opened_dir); reset($this->opened_dir); @@ -551,39 +739,42 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper * This flag is set in response to calls to lstat(), is_link(), or filetype(). * - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set, * you are responsible for reporting errors using the trigger_error() function during stating of the path. + * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper! * @return array */ - function url_stat ( $url, $flags ) + static function url_stat ( $url, $flags ) { if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags)"); - - /*return array( - 'mode' => 0100666, - 'name' => basename(parse_url($path,PHP_URL_PATH)), - 'size' => strlen(basename(parse_url($path,PHP_URL_PATH))), - 'nlink' => 1, - 'uid' => 1000, - 'gid' => 100, - 'mtime' => time(), - );*/ + $path = parse_url($url,PHP_URL_PATH); + + // check if we already have the info from the last dir_open call, as the old vfs reads it anyway from the db + if (self::$stat_cache && isset(self::$stat_cache[$path])) + { + return self::_vfsinfo2stat(self::$stat_cache[$path]); + } + if (!is_object(self::$old_vfs)) { self::$old_vfs =& new vfs_home(); } - $path = parse_url($url,PHP_URL_PATH); - list($info) = self::$old_vfs->ls(array( 'string' => $path, 'relatives' => array(RELATIVE_ROOT), // filename is relative to the vfs-root 'checksubdirs' => false, 'nofiles' => true, - //'orderby' => '', + //'orderby' => 'name', //'mime_type' => '', )); //print_r($info); + if (!$info) + { + self::remove_password($url); + if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$flags) file or directory not found!"); + return false; + } - return $info ? $this->vfsinfo2stat($info) : false; + return self::_vfsinfo2stat($info); } /** @@ -595,13 +786,13 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper */ function dir_readdir ( ) { - if (self::LOG_LEVEL > 1) error_log(__METHOD__."($this->opened_dir_path)"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )"); if (!is_array($this->opened_dir)) return false; $file = current($this->opened_dir); next($this->opened_dir); - return $file ? $file['name'] : false; + return $file ? $file : false; } /** @@ -614,7 +805,7 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper */ function dir_rewinddir ( ) { - if (self::LOG_LEVEL > 1) error_log(__METHOD__."($this->opened_dir_path)"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )"); if (!is_array($this->opened_dir)) return false; @@ -632,11 +823,11 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper */ function dir_closedir ( ) { - if (self::LOG_LEVEL > 1) error_log(__METHOD__."($this->opened_dir_path)"); + if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )"); if (!is_array($this->opened_dir)) return false; - $this->opened_dir = $this->opened_dir_path = null; + $this->opened_dir = null; return true; } @@ -647,18 +838,20 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper * @param array $info * @return array */ - function vfsinfo2stat($info) + static private function _vfsinfo2stat($info) { $stat = array( 'ino' => $info['file_id'], 'name' => $info['name'], - 'mode' => $info['mime_type'] == 'Directory' ? 040700 : 0100600, + 'mode' => $info['owner_id'] > 0 ? + ($info['mime_type'] == self::DIR_MIME_TYPE ? 040700 : 0100600) : + ($info['mime_type'] == self::DIR_MIME_TYPE ? 040070 : 0100060), 'size' => $info['size'], 'uid' => $info['owner_id'] > 0 ? $info['owner_id'] : 0, 'gid' => $info['owner_id'] < 0 ? $info['owner_id'] : 0, 'mtime' => strtotime($info['modified'] ? $info['modified'] : $info['created']), 'ctime' => strtotime($info['created']), - 'nlink' => $info['mime_type'] == 'Directory' ? 2 : 1, + 'nlink' => $info['mime_type'] == self::DIR_MIME_TYPE ? 2 : 1, ); //print_r($stat); return $stat; @@ -680,6 +873,22 @@ class oldvfs_stream_wrapper implements iface_stream_wrapper return $func_overload & 2 ? mb_substr($str,$start,$length,'ascii') : substr($str,$start,$length); } + + /** + * Replace the password of an url with '...' for error messages + * + * @param string &$url + */ + static private function remove_password(&$url) + { + $parts = parse_url($url); + + if ($parts['pass'] || $parts['scheme']) + { + $url = $parts['scheme'].'://'.($parts['user'] ? $parts['user'].($parts['pass']?':...':'').'@' : ''). + $parts['host'].$parts['path']; + } + } } stream_register_wrapper('oldvfs','oldvfs_stream_wrapper');