2008-02-18 07:52:07 +01:00
< ? php
/**
* eGroupWare API : VFS - new DB based VFS stream wrapper
*
* @ link http :// www . egroupware . org
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package api
* @ subpackage vfs
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2010-02-07 07:19:56 +01:00
* @ copyright ( c ) 2008 - 10 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2008-02-18 07:52:07 +01:00
* @ version $Id $
*/
/**
* eGroupWare API : VFS - new DB based VFS stream wrapper
2008-04-18 10:42:11 +02:00
*
2010-03-17 10:11:28 +01:00
* The sqlfs stream wrapper has 2 operation modi :
2008-02-18 07:52:07 +01:00
* - content of files is stored in the filesystem ( eGW ' s files_dir ) ( default )
2008-10-01 20:01:45 +02:00
* - content of files is stored as BLOB in the DB ( can be enabled by mounting sqlfs ://... ? storage = db )
* please note the current ( php5 . 2.6 ) problems :
* a ) retriving files via streams does NOT work for PDO_mysql ( bindColum (,, PDO :: PARAM_LOB ) does NOT work , string returned )
* ( there ' s a workaround implemented , but it requires to allocate memory for the whole file ! )
* b ) uploading / writing files > 1 M fail on PDOStatement :: execute () ( setting PDO :: MYSQL_ATTR_MAX_BUFFER_SIZE does NOT help )
* ( not sure if that ' s a bug in PDO / PDO_mysql or an accepted limitation )
2008-02-18 07:52:07 +01:00
* - content of files is versioned ( and stored in the DB ) NOT YET IMPLEMENTED
* In the future it will be possible to activate eg . versioning in parts of the filesystem , via mount options in the vfs
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* I use the PDO DB interface , as it allows to access BLOB ' s as streams ( avoiding to hold them complete in memory ) .
2008-04-18 10:42:11 +02:00
*
2008-04-14 07:52:24 +02:00
* The stream wrapper interface is according to the docu on php . net
2008-04-18 10:42:11 +02:00
*
2009-03-19 21:12:35 +01:00
* @ link http :// www . php . net / manual / en / function . stream - wrapper - register . php
2008-02-18 07:52:07 +01:00
*/
2008-04-18 10:42:11 +02:00
class sqlfs_stream_wrapper implements iface_stream_wrapper
2008-02-18 07:52:07 +01:00
{
/**
* Mime type of directories , the old vfs uses 'Directory' , while eg . WebDAV uses 'httpd/unix-directory'
*/
const DIR_MIME_TYPE = 'httpd/unix-directory' ;
2009-03-19 21:12:35 +01:00
/**
* Mime type for symlinks
*/
const SYMLINK_MIME_TYPE = 'application/x-symlink' ;
2008-02-18 07:52:07 +01:00
/**
* Scheme / protocoll used for this stream - wrapper
*/
const SCHEME = 'sqlfs' ;
/**
* Does url_stat returns a mime type , or has it to be determined otherwise ( string with attribute name )
*/
const STAT_RETURN_MIME_TYPE = 'mime' ;
/**
* Our tablename
*/
const TABLE = 'egw_sqlfs' ;
2008-10-05 19:07:36 +02:00
/**
* Name of our property table
*/
const PROPS_TABLE = 'egw_sqlfs_props' ;
2008-02-18 07:52:07 +01:00
/**
* mode - bits , which have to be set for files
*/
const MODE_FILE = 0100000 ;
/**
* mode - bits , which have to be set for directories
*/
const MODE_DIR = 040000 ;
2009-03-19 21:12:35 +01:00
/**
* mode - bits , which have to be set for links
*/
const MODE_LINK = 0120000 ;
2008-02-18 07:52:07 +01:00
/**
* How much should be logged to the apache error - log
*
* 0 = Nothing
* 1 = only errors
* 2 = all function calls and errors ( contains passwords too ! )
2009-03-27 20:10:16 +01:00
* 3 = log line numbers in sql statements
2008-02-18 07:52:07 +01:00
*/
2009-03-19 21:12:35 +01:00
const LOG_LEVEL = 1 ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* We store the content in the DB ( no versioning )
*/
const STORE2DB = 1 ;
/**
* We store the content in the filesystem ( egw_info / server / files_dir ) ( no versioning )
*/
const STORE2FS = 2 ;
/**
* default for operation , change that if you want to test with STORE2DB atm
*/
2008-10-01 20:01:45 +02:00
const DEFAULT_OPERATION = self :: STORE2FS ;
2008-02-18 07:52:07 +01:00
2008-10-01 20:01:45 +02:00
/**
* operation mode of the opened file
*
* @ var int
*/
protected $operation = self :: DEFAULT_OPERATION ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* optional context param when opening the stream , null if no context passed
*
* @ var mixed
*/
var $context ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* Path off the file opened by stream_open
*
* @ var string
*/
protected $opened_path ;
/**
* Mode of the file opened by stream_open
*
* @ var int
*/
protected $opened_mode ;
/**
* Stream of the opened file , either from the DB via PDO or the filesystem
*
* @ var resource
*/
protected $opened_stream ;
/**
* fs_id of opened file
*
* @ var int
*/
protected $opened_fs_id ;
/**
2009-04-07 21:42:46 +02:00
* Cache containing stat - infos from previous url_stat calls AND dir_opendir calls
2008-04-18 10:42:11 +02:00
*
2009-04-07 21:42:46 +02:00
* It ' s values are the columns read from the DB ( fs_ * ), not the ones returned by url_stat !
2008-02-18 07:52:07 +01:00
*
* @ var array $path => info - array pairs
*/
2010-02-07 07:19:56 +01:00
static protected $stat_cache = array ();
2008-02-18 07:52:07 +01:00
/**
* Reference to the PDO object we use
*
* @ var PDO
*/
2010-02-07 07:19:56 +01:00
static protected $pdo ;
2008-02-18 07:52:07 +01:00
/**
* Array with filenames of dir opened with dir_opendir
*
* @ var array
*/
protected $opened_dir ;
2008-04-18 10:42:11 +02:00
2009-07-01 17:17:36 +02:00
/**
* Extra columns added since the intitial introduction of sqlfs
*
* Can be set to empty , so get queries running on old versions of sqlfs , eg . for schema updates
*
* @ var string ;
*/
2009-07-01 18:17:44 +02:00
static public $extra_columns = ',fs_link' ;
2009-07-01 17:17:36 +02:00
2008-02-18 07:52:07 +01:00
/**
* This method is called immediately after your stream object is created .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ param string $url URL that was passed to fopen () and that this object is expected to retrieve
* @ param string $mode mode used to open the file , as detailed for fopen ()
2008-04-18 10:42:11 +02:00
* @ param int $options additional flags set by the streams API ( or ' ed together ) :
2008-02-18 07:52:07 +01:00
* - STREAM_USE_PATH If path is relative , search for the resource using the include_path .
2008-04-18 10:42:11 +02:00
* - STREAM_REPORT_ERRORS If this flag is set , you are responsible for raising errors using trigger_error () during opening of the stream .
2008-02-18 07:52:07 +01:00
* If this flag is not set , you should not raise any errors .
2010-02-07 07:19:56 +01:00
* @ param string & $opened_path full path of the file / resource , if the open was successfull and STREAM_USE_PATH was set
* @ param array $overwrite_new = null if set create new file with values overwriten by the given ones
* @ param string $class = __CLASS__ class to use to call static methods , eg url_stat ( workaround for no late static binding in php < 5.3 )
* @ todo remove $class parameter and use static :: url_stat () once we require PHP5 . 3 !
2008-02-18 07:52:07 +01:00
* @ return boolean true if the ressource was opened successful , otherwise false
*/
2010-02-07 07:19:56 +01:00
function stream_open ( $url , $mode , $options , & $opened_path , array $overwrite_new = null , $class = __CLASS__ )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url , $mode , $options ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-10-01 20:01:45 +02:00
$this -> operation = self :: url2operation ( $url );
2008-04-14 07:52:24 +02:00
$dir = egw_vfs :: dirname ( $url );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$this -> opened_path = $path ;
2008-10-10 19:00:03 +02:00
$this -> opened_mode = $mode = str_replace ( 'b' , '' , $mode ); // we are always binary, like every Linux system
2008-02-18 07:52:07 +01:00
$this -> opened_stream = null ;
2010-03-17 10:11:28 +01:00
2010-02-07 07:19:56 +01:00
if ( ! is_null ( $overwrite_new ) || ! ( $stat = call_user_func ( array ( $class , 'url_stat' ), $path , STREAM_URL_STAT_QUIET )) || $mode [ 0 ] == 'x' ) // file not found or file should NOT exist
2008-02-18 07:52:07 +01:00
{
if ( $mode [ 0 ] == 'r' || // does $mode require the file to exist (r,r+)
$mode [ 0 ] == 'x' || // or file should not exist, but does
2010-02-07 07:19:56 +01:00
! ( $dir_stat = call_user_func ( array ( $class , 'url_stat' ), $dir , STREAM_URL_STAT_QUIET )) || // or parent dir does not exist create it
2009-04-07 21:42:46 +02:00
! egw_vfs :: check_access ( $dir , egw_vfs :: WRITABLE , $dir_stat )) // or we are not allowed to create it
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url );
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $mode , $options ) file does not exist or can not be created! " );
2010-02-07 07:19:56 +01:00
if (( $options & STREAM_REPORT_ERRORS ))
2008-02-18 07:52:07 +01:00
{
trigger_error ( __METHOD__ . " ( $url , $mode , $options ) file does not exist or can not be created! " , E_USER_WARNING );
}
$this -> opened_stream = $this -> opened_path = $this -> opened_mode = null ;
return false ;
}
// new file --> create it in the DB
2010-02-07 07:19:56 +01:00
$query = 'INSERT INTO ' . self :: TABLE . ' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size,fs_active' .
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_active)' ;
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . ': ' . __LINE__ . ' */ ' . $query ;
$stmt = self :: $pdo -> prepare ( $query );
2008-02-18 07:52:07 +01:00
$values = array (
2009-04-09 15:24:34 +02:00
'fs_name' => egw_vfs :: basename ( $path ),
2008-02-18 07:52:07 +01:00
'fs_dir' => $dir_stat [ 'ino' ],
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
2008-02-26 09:51:42 +01:00
// we use the mode of the dir, so files in group dirs stay accessible by all members
2008-02-18 07:52:07 +01:00
'fs_mode' => $dir_stat [ 'mode' ] & 0666 ,
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
2008-02-26 09:51:42 +01:00
// 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
2008-02-18 07:52:07 +01:00
'fs_gid' => $dir_stat [ 'gid' ],
'fs_created' => self :: _pdo_timestamp ( time ()),
'fs_modified' => self :: _pdo_timestamp ( time ()),
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
2008-02-26 09:51:42 +01:00
'fs_creator' => egw_vfs :: $user ,
2008-10-01 20:01:45 +02:00
'fs_mime' => 'application/octet-stream' , // required NOT NULL!
2008-10-15 08:00:38 +02:00
'fs_size' => 0 ,
2010-02-07 07:19:56 +01:00
'fs_active' => self :: _pdo_boolean ( true ),
2008-02-18 07:52:07 +01:00
);
2010-02-07 07:19:56 +01:00
if ( $overwrite_new ) $values = array_merge ( $values , $overwrite_new );
2009-03-30 13:17:30 +02:00
if ( ! $stmt -> execute ( $values ) || ! ( $this -> opened_fs_id = self :: $pdo -> lastInsertId ( 'egw_sqlfs_fs_id_seq' )))
2008-03-02 22:44:15 +01:00
{
$this -> opened_stream = $this -> opened_path = $this -> opened_mode = null ;
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $mode , $options ) execute() failed: " . self :: $pdo -> errorInfo ());
2008-03-02 22:44:15 +01:00
return false ;
}
2008-10-01 20:01:45 +02:00
if ( $this -> operation == self :: STORE2DB )
{
// we buffer all write operations in a temporary file, which get's written on close
$this -> opened_stream = tmpfile ();
}
2008-10-03 14:18:19 +02:00
// create the hash-dirs, if they not yet exist
elseif ( ! file_exists ( $fs_dir = dirname ( self :: _fs_path ( $this -> opened_fs_id ))))
{
2009-01-29 19:58:52 +01:00
$umaskbefore = umask ();
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " about to call mkdir for $fs_dir # Present UMASK: " . decoct ( $umaskbefore ) . " called from: " . function_backtrace ());
2009-01-29 19:58:52 +01:00
self :: mkdir_recursive ( $fs_dir , 0700 , true );
2008-10-03 14:18:19 +02:00
}
2008-02-18 07:52:07 +01:00
}
2010-04-27 12:38:16 +02:00
// check if opend file is a directory
elseif ( $stat && ( $stat [ 'mode' ] & self :: MODE_DIR ) == self :: MODE_DIR )
{
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $mode , $options ) Is a directory! " );
if (( $options & STREAM_REPORT_ERRORS ))
{
trigger_error ( __METHOD__ . " ( $url , $mode , $options ) Is a directory! " , E_USER_WARNING );
}
$this -> opened_stream = $this -> opened_path = $this -> opened_mode = null ;
return false ;
}
2008-02-18 07:52:07 +01:00
else
{
2008-11-09 17:10:36 +01:00
if ( $mode == 'r' && ! egw_vfs :: check_access ( $url , egw_vfs :: READABLE , $stat ) ||// we are not allowed to read
$mode != 'r' && ! egw_vfs :: check_access ( $url , egw_vfs :: WRITABLE , $stat )) // or edit it
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url );
2008-11-09 17:10:36 +01:00
$op = $mode == 'r' ? 'read' : 'edited' ;
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $mode , $options ) file can not be $op ! " );
2010-02-07 07:19:56 +01:00
if (( $options & STREAM_REPORT_ERRORS ))
2008-02-18 07:52:07 +01:00
{
2008-11-09 17:10:36 +01:00
trigger_error ( __METHOD__ . " ( $url , $mode , $options ) file can not be $op ! " , E_USER_WARNING );
2008-02-18 07:52:07 +01:00
}
$this -> opened_stream = $this -> opened_path = $this -> opened_mode = null ;
2008-04-18 10:42:11 +02:00
return false ;
2008-02-18 07:52:07 +01:00
}
2008-05-13 07:50:11 +02:00
$this -> opened_fs_id = $stat [ 'ino' ];
2008-10-01 20:01:45 +02:00
if ( $this -> operation == self :: STORE2DB )
{
$stmt = self :: $pdo -> prepare ( $sql = 'SELECT fs_content FROM ' . self :: TABLE . ' WHERE fs_id=?' );
$stmt -> execute ( array ( $stat [ 'ino' ]));
$stmt -> bindColumn ( 1 , $this -> opened_stream , PDO :: PARAM_LOB );
$stmt -> fetch ( PDO :: FETCH_BOUND );
// hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913)
// PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-(
if ( is_string ( $this -> opened_stream ))
{
$name = md5 ( $url );
$GLOBALS [ $name ] =& $this -> opened_stream ; unset ( $this -> opened_stream );
require_once ( EGW_API_INC . '/class.global_stream_wrapper.inc.php' );
$this -> opened_stream = fopen ( 'global://' . $name , 'r' );
unset ( $GLOBALS [ $name ]); // unset it, so it does not use up memory, once the stream is closed
}
//echo 'gettype($this->opened_stream)='; var_dump($this->opened_stream);
}
2008-02-18 07:52:07 +01:00
}
// do we operate directly on the filesystem
if ( $this -> operation == self :: STORE2FS )
{
2009-01-29 19:58:52 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " fopen (may create a directory? mkdir) ( $this->opened_fs_id , $mode , $options ) " );
2008-10-03 14:18:19 +02:00
$this -> opened_stream = fopen ( self :: _fs_path ( $this -> opened_fs_id ), $mode );
2008-02-18 07:52:07 +01:00
}
if ( $mode [ 0 ] == 'a' ) // append modes: a, a+
{
$this -> stream_seek ( 0 , SEEK_END );
}
2008-10-01 20:01:45 +02:00
if ( ! is_resource ( $this -> opened_stream )) error_log ( __METHOD__ . " ( $url , $mode , $options ) NO stream, returning false! " );
2008-02-18 07:52:07 +01:00
return is_resource ( $this -> opened_stream );
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
2008-04-18 10:42:11 +02:00
* This method is called when the stream is closed , using fclose () .
*
2008-02-18 07:52:07 +01:00
* You must release any resources that were locked or allocated by the stream .
*/
function stream_close ( )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " () " );
2008-04-18 10:42:11 +02:00
2008-03-02 22:44:15 +01:00
if ( is_null ( $this -> opened_path ) || ! is_resource ( $this -> opened_stream ) || ! $this -> opened_fs_id )
2008-02-18 07:52:07 +01:00
{
return false ;
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( $this -> opened_mode != 'r' )
{
$this -> stream_seek ( 0 , SEEK_END );
2008-04-18 10:42:11 +02:00
2008-10-01 20:01:45 +02:00
// we need to update the mime-type, size and content (if STORE2DB)
2008-02-18 07:52:07 +01:00
$values = array (
2009-03-30 13:17:30 +02:00
'fs_size' => $this -> stream_tell (),
2008-02-18 07:52:07 +01:00
// todo: analyse the file for the mime-type
2009-04-17 08:25:33 +02:00
'fs_mime' => mime_magic :: filename2mime ( $this -> opened_path ),
2009-03-30 13:17:30 +02:00
'fs_id' => $this -> opened_fs_id ,
'fs_modifier' => egw_vfs :: $user ,
2009-04-16 18:32:30 +02:00
'fs_modified' => self :: _pdo_timestamp ( time ()),
2008-02-18 07:52:07 +01:00
);
2008-04-18 10:42:11 +02:00
2008-10-01 20:01:45 +02:00
if ( $this -> operation == self :: STORE2FS )
{
2009-04-16 18:32:30 +02:00
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_size=:fs_size,fs_mime=:fs_mime,fs_modifier=:fs_modifier,fs_modified=:fs_modified WHERE fs_id=:fs_id' );
2010-03-04 13:05:42 +01:00
if ( ! ( $ret = $stmt -> execute ( $values )))
{
error_log ( __METHOD__ . " () execute() failed! errorInfo()= " . array2string ( self :: $pdo -> errorInfo ()));
}
2008-10-01 20:01:45 +02:00
}
else
{
2009-04-16 18:32:30 +02:00
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_size=:fs_size,fs_mime=:fs_mime,fs_modifier=:fs_modifier,fs_modified=:fs_modified,fs_content=:fs_content WHERE fs_id=:fs_id' );
2008-10-01 20:01:45 +02:00
$this -> stream_seek ( 0 , SEEK_SET ); // rewind to the start
2010-03-04 13:05:42 +01:00
foreach ( $values as $name => & $value )
{
$stmt -> bindParam ( $name , $value );
}
2009-03-30 13:17:30 +02:00
$stmt -> bindParam ( 'fs_content' , $this -> opened_stream , PDO :: PARAM_LOB );
2010-03-04 13:05:42 +01:00
if ( ! ( $ret = $stmt -> execute ()))
{
error_log ( __METHOD__ . " () execute() failed! errorInfo()= " . array2string ( self :: $pdo -> errorInfo ()));
}
2008-10-01 20:01:45 +02:00
}
2008-02-18 07:52:07 +01:00
}
2008-10-01 20:01:45 +02:00
$ret = fclose ( $this -> opened_stream ) && $ret ;
2009-03-27 20:10:16 +01:00
unset ( self :: $stat_cache [ $this -> opened_path ]);
2008-02-18 07:52:07 +01:00
$this -> opened_stream = $this -> opened_path = $this -> opened_mode = $this -> opend_fs_id = null ;
2008-10-01 20:01:45 +02:00
$this -> operation = self :: DEFAULT_OPERATION ;
2008-02-18 07:52:07 +01:00
return $ret ;
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* This method is called in response to fread () and fgets () calls on the stream .
2008-04-18 10:42:11 +02:00
*
* You must return up - to count bytes of data from the current read / write position as a string .
* If there are less than count bytes available , return as many as are available .
* If no more data is available , return either FALSE or an empty string .
2008-02-18 07:52:07 +01:00
* You must also update the read / write position of the stream by the number of bytes that were successfully read .
*
* @ param int $count
* @ return string / false up to count bytes read or false on EOF
*/
function stream_read ( $count )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $count ) pos= $this->opened_pos " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( is_resource ( $this -> opened_stream ))
{
return fread ( $this -> opened_stream , $count );
}
return false ;
}
/**
* This method is called in response to fwrite () calls on the stream .
2008-04-18 10:42:11 +02:00
*
* You should store data into the underlying storage used by your stream .
* If there is not enough room , try to store as many bytes as possible .
* You should return the number of bytes that were successfully stored in the stream , or 0 if none could be stored .
2008-02-18 07:52:07 +01:00
* You must also update the read / write position of the stream by the number of bytes that were successfully written .
*
* @ param string $data
* @ return integer
*/
2008-04-18 10:42:11 +02:00
function stream_write ( $data )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $data ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( is_resource ( $this -> opened_stream ))
{
return fwrite ( $this -> opened_stream , $data );
}
return false ;
}
/**
* This method is called in response to feof () calls on the stream .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* Important : PHP 5.0 introduced a bug that wasn ' t fixed until 5.1 : the return value has to be the oposite !
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* if ( version_compare ( PHP_VERSION , '5.0' , '>=' ) && version_compare ( PHP_VERSION , '5.1' , '<' ))
* {
* $eof = ! $eof ;
* }
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ return boolean true if the read / write position is at the end of the stream and no more data availible , false otherwise
*/
2008-04-18 10:42:11 +02:00
function stream_eof ( )
2008-02-18 07:52:07 +01:00
{
if ( is_resource ( $this -> opened_stream ))
{
return feof ( $this -> opened_stream );
}
return false ;
}
/**
* This method is called in response to ftell () calls on the stream .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ return integer current read / write position of the stream
*/
2008-04-18 10:42:11 +02:00
function stream_tell ( )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " () " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( is_resource ( $this -> opened_stream ))
{
return ftell ( $this -> opened_stream );
}
return false ;
}
/**
* This method is called in response to fseek () calls on the stream .
*
2008-04-18 10:42:11 +02:00
* You should update the read / write position of the stream according to offset and whence .
* See fseek () for more information about these parameters .
*
2008-02-18 07:52:07 +01:00
* @ param integer $offset
* @ param integer $whence SEEK_SET - Set position equal to offset bytes
* SEEK_CUR - Set position to current location plus offset .
* SEEK_END - Set position to end - of - file plus offset . ( To move to a position before the end - of - file , you need to pass a negative value in offset . )
* @ return boolean TRUE if the position was updated , FALSE otherwise .
*/
function stream_seek ( $offset , $whence )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $offset , $whence ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( is_resource ( $this -> opened_stream ))
{
return fseek ( $this -> opened_stream , $offset , $whence );
}
return false ;
}
/**
* This method is called in response to fflush () calls on the stream .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* If you have cached data in your stream but not yet stored it into the underlying storage , you should do so now .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ return booelan TRUE if the cached data was successfully stored ( or if there was no data to store ), or FALSE if the data could not be stored .
*/
function stream_flush ( )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " () " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( is_resource ( $this -> opened_stream ))
{
return fflush ( $this -> opened_stream );
}
return false ;
}
/**
* This method is called in response to fstat () calls on the stream .
2008-04-18 10:42:11 +02:00
*
* If you plan to use your wrapper in a require_once you need to define stream_stat () .
2008-02-18 07:52:07 +01:00
* If you plan to allow any other tests like is_file () / is_dir (), you have to define url_stat () .
2008-04-18 10:42:11 +02:00
* stream_stat () must define the size of the file , or it will never be included .
2008-02-18 07:52:07 +01:00
* url_stat () must define mode , or is_file () / is_dir () / is_executable (), and any of those functions affected by clearstatcache () simply won ' t work .
2008-04-18 10:42:11 +02:00
* It ' s not documented , but directories must be a mode like 040777 ( octal ), and files a mode like 0100666.
* If you wish the file to be executable , use 7 s instead of 6 s .
* The last 3 digits are exactly the same thing as what you pass to chmod .
* 040000 defines a directory , and 0100000 defines a file .
2008-02-18 07:52:07 +01:00
*
* @ return array containing the same values as appropriate for the stream .
*/
function stream_stat ( )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $this->opened_path ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
return $this -> url_stat ( $this -> opened_path , 0 );
}
/**
* This method is called in response to unlink () calls on URL paths associated with the wrapper .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* 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 $url
* @ return boolean TRUE on success or FALSE on failure
*/
2008-10-01 20:01:45 +02:00
static function unlink ( $url )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02:00
2009-03-19 21:12:35 +01:00
if ( ! ( $stat = self :: url_stat ( $path , STREAM_URL_STAT_LINK )) || ! egw_vfs :: check_access ( dirname ( $path ), egw_vfs :: WRITABLE ))
2008-02-18 07:52:07 +01:00
{
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
}
$stmt = self :: $pdo -> prepare ( 'DELETE FROM ' . self :: TABLE . ' WHERE fs_id=:fs_id' );
unset ( self :: $stat_cache [ $path ]);
2009-03-30 13:17:30 +02:00
if (( $ret = $stmt -> execute ( array ( 'fs_id' => $stat [ 'ino' ]))))
2008-02-18 07:52:07 +01:00
{
2010-03-17 10:11:28 +01:00
if ( self :: url2operation ( $url ) == self :: STORE2FS &&
2010-01-21 23:56:57 +01:00
( $stat [ 'mode' ] & self :: MODE_LINK ) != self :: MODE_LINK )
2009-03-19 21:12:35 +01:00
{
unlink ( self :: _fs_path ( $stat [ 'ino' ]));
}
2008-10-05 19:07:36 +02:00
// delete props
unset ( $stmt );
$stmt = self :: $pdo -> prepare ( 'DELETE FROM ' . self :: PROPS_TABLE . ' WHERE fs_id=?' );
$stmt -> execute ( array ( $stat [ 'ino' ]));
2008-02-18 07:52:07 +01:00
}
return $ret ;
}
/**
* This method is called in response to rename () calls on URL paths associated with the wrapper .
2008-04-18 10:42:11 +02:00
*
* It should attempt to rename the item specified by path_from to the specification given by path_to .
2008-02-18 07:52:07 +01:00
* In order for the appropriate error message to be returned , do not define this method if your wrapper does not support renaming .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* The regular filesystem stream - wrapper returns an error , if $url_from and $url_to are not either both files or both dirs !
*
* @ param string $url_from
* @ param string $url_to
2009-12-01 15:51:13 +01:00
* @ param string $class = __CLASS__ class to use to call static methods , eg url_stat ( workaround for no late static binding in php < 5.3 )
* @ todo remove $class parameter and use static :: url_stat () and static :: unlink () once we require PHP5 . 3 !
2008-02-18 07:52:07 +01:00
* @ return boolean TRUE on success or FALSE on failure
*/
2009-12-01 15:51:13 +01:00
static function rename ( $url_from , $url_to , $class = __CLASS__ )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url_from , $url_to ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path_from = parse_url ( $url_from , PHP_URL_PATH );
2010-02-15 05:35:32 +01:00
$from_dir = dirname ( $path_from );
2008-02-18 07:52:07 +01:00
$path_to = parse_url ( $url_to , PHP_URL_PATH );
2008-04-14 07:52:24 +02:00
$to_dir = dirname ( $path_to );
2008-10-01 20:01:45 +02:00
$operation = self :: url2operation ( $url_from );
2008-04-18 10:42:11 +02:00
2009-12-01 15:51:13 +01:00
// we have to use array($class,'url_stat'), as $class.'::url_stat' requires PHP 5.2.3 and we currently only require 5.2+
2010-03-17 10:11:28 +01:00
if ( ! ( $from_stat = call_user_func ( array ( $class , 'url_stat' ), $path_from , 0 )) ||
2010-02-15 05:35:32 +01:00
! egw_vfs :: check_access ( $from_dir , egw_vfs :: WRITABLE , $from_dir_stat = call_user_func ( array ( $class , 'url_stat' ), $from_dir , 0 )))
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url_from );
self :: _remove_password ( $url_to );
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url_from , $url_to ): $path_from permission denied! " );
return false ; // no permission or file does not exist
}
2009-12-01 15:51:13 +01:00
if ( ! egw_vfs :: check_access ( $to_dir , egw_vfs :: WRITABLE , $to_dir_stat = call_user_func ( array ( $class , 'url_stat' ), $to_dir , 0 )))
2008-02-18 07:52:07 +01:00
{
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 parent-dir does not exist
}
// the filesystem stream-wrapper does NOT allow to rename files to directories, as this makes problems
// for our vfs too, we abort here with an error, like the filesystem one does
2009-12-01 15:51:13 +01:00
if (( $to_stat = call_user_func ( array ( $class , 'url_stat' ), $path_to , 0 )) &&
2008-02-18 07:52:07 +01:00
( $to_stat [ 'mime' ] === self :: DIR_MIME_TYPE ) !== ( $from_stat [ 'mime' ] === self :: DIR_MIME_TYPE ))
{
self :: _remove_password ( $url_from );
self :: _remove_password ( $url_to );
$is_dir = $to_stat [ 'mime' ] === self :: DIR_MIME_TYPE ? 'a' : 'no' ;
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
}
2008-07-27 20:58:41 +02:00
// if destination file already exists, delete it
2009-12-01 15:51:13 +01:00
if ( $to_stat && ! call_user_func ( array ( $class , 'unlink' ), $url_to , $operation ))
2008-07-27 20:58:41 +02:00
{
self :: _remove_password ( $url_to );
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url_to , $url_from ) can't unlink existing $url_to ! " );
return false ;
}
2008-10-03 14:18:19 +02:00
unset ( self :: $stat_cache [ $path_from ]);
2009-03-27 20:10:16 +01:00
unset ( self :: $stat_cache [ $path_to ]);
2008-10-03 14:18:19 +02:00
2010-02-15 05:35:32 +01:00
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_dir=:fs_dir,fs_name=:fs_name WHERE fs_dir=:old_dir AND fs_name=:old_name' );
2009-12-01 15:51:13 +01:00
$ok = $stmt -> execute ( array (
2010-02-15 05:35:32 +01:00
'fs_dir' => $to_dir_stat [ 'ino' ],
'fs_name' => egw_vfs :: basename ( $path_to ),
'old_dir' => $from_dir_stat [ 'ino' ],
'old_name' => $from_stat [ 'name' ],
2008-10-03 14:18:19 +02:00
));
2009-12-01 15:51:13 +01:00
unset ( $stmt );
// check if extension changed and update mime-type in that case (as we currently determine mime-type by it's extension!)
// fixes eg. problems with MsWord storing file with .tmp extension and then renaming to .doc
if ( $ok && ( $new_mime = egw_vfs :: mime_content_type ( $url_to , true )) != egw_vfs :: mime_content_type ( $url_to ))
{
//echo "<p>egw_vfs::nime_content_type($url_to,true) = $new_mime</p>\n";
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_mime=:fs_mime WHERE fs_id=:fs_id' );
$stmt -> execute ( array (
'fs_mime' => $new_mime ,
'fs_id' => $from_stat [ 'ino' ],
));
unset ( self :: $stat_cache [ $path_to ]);
}
return $ok ;
2008-02-18 07:52:07 +01:00
}
2009-01-29 19:58:52 +01:00
/**
* due to problems with recursive directory creation , we have our own here
*/
2010-02-15 05:35:32 +01:00
private static function mkdir_recursive ( $pathname , $mode , $depth = 0 )
2009-01-29 19:58:52 +01:00
{
$maxdepth = 10 ;
$depth2propagate = ( int ) $depth + 1 ;
if ( $depth2propagate > $maxdepth ) return is_dir ( $pathname );
is_dir ( dirname ( $pathname )) || self :: mkdir_recursive ( dirname ( $pathname ), $mode , $depth2propagate );
return is_dir ( $pathname ) || @ mkdir ( $pathname , $mode );
}
2008-02-18 07:52:07 +01:00
/**
* This method is called in response to mkdir () calls on URL paths associated with the wrapper .
2008-04-18 10:42:11 +02:00
*
* 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 .
2008-02-18 07:52:07 +01:00
*
* @ 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
*/
2008-10-01 20:01:45 +02:00
static function mkdir ( $url , $mode , $options )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url , $mode , $options ) " );
2009-01-29 19:58:52 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " called from: " . function_backtrace ());
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
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! " );
2010-02-07 07:19:56 +01:00
if ( ! ( $options & STREAM_REPORT_ERRORS ))
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
2008-02-26 09:51:42 +01:00
{
2008-10-26 08:37:37 +01:00
//throw new Exception(__METHOD__."('$url',$mode,$options) already exist!");
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
2008-02-26 09:51:42 +01:00
trigger_error ( __METHOD__ . " (' $url ', $mode , $options ) already exist! " , E_USER_WARNING );
}
return false ;
}
2008-10-02 13:01:28 +02:00
$parent_path = dirname ( $path );
if (( $query = parse_url ( $url , PHP_URL_QUERY ))) $parent_path .= '?' . $query ;
$parent = self :: url_stat ( $parent_path , STREAM_URL_STAT_QUIET );
2008-02-18 07:52:07 +01:00
2008-04-18 10:42:11 +02:00
// check if we should also create all non-existing path components and our parent does not exist,
2008-02-18 07:52:07 +01:00
// if yes call ourself recursive with the parent directory
2009-04-07 21:42:46 +02:00
if (( $options & STREAM_MKDIR_RECURSIVE ) && $parent_path != '/' && ! $parent )
2008-02-18 07:52:07 +01:00
{
2009-01-29 19:58:52 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " creating parents: $parent_path , $mode " );
2008-04-14 07:52:24 +02:00
if ( ! self :: mkdir ( $parent_path , $mode , $options ))
2008-02-18 07:52:07 +01:00
{
return false ;
}
2008-10-02 13:01:28 +02:00
$parent = self :: url_stat ( $parent_path , 0 );
2008-02-18 07:52:07 +01:00
}
2008-04-14 07:52:24 +02:00
if ( ! $parent || ! egw_vfs :: check_access ( $parent_path , egw_vfs :: WRITABLE , $parent ))
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url );
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
2008-02-26 09:51:42 +01:00
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " (' $url ', $mode , $options ) permission denied! " );
2010-02-07 07:19:56 +01:00
if ( ! ( $options & STREAM_REPORT_ERRORS ))
2008-02-18 07:52:07 +01:00
{
trigger_error ( __METHOD__ . " (' $url ', $mode , $options ) permission denied! " , E_USER_WARNING );
}
return false ; // no permission or file does not exist
}
2009-03-27 20:10:16 +01:00
unset ( self :: $stat_cache [ $path ]);
2008-02-18 07:52:07 +01:00
$stmt = self :: $pdo -> prepare ( 'INSERT INTO ' . self :: TABLE . ' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified,fs_creator' .
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_size,:fs_mime,:fs_created,:fs_modified,:fs_creator)' );
2009-04-09 15:24:34 +02:00
return $stmt -> execute ( array (
'fs_name' => egw_vfs :: basename ( $path ),
2009-03-30 13:17:30 +02:00
'fs_dir' => $parent [ 'ino' ],
'fs_mode' => $parent [ 'mode' ],
'fs_uid' => $parent [ 'uid' ],
'fs_gid' => $parent [ 'gid' ],
'fs_size' => 0 ,
'fs_mime' => self :: DIR_MIME_TYPE ,
'fs_created' => self :: _pdo_timestamp ( time ()),
'fs_modified' => self :: _pdo_timestamp ( time ()),
'fs_creator' => egw_vfs :: $user ,
2008-10-03 14:18:19 +02:00
));
2008-02-18 07:52:07 +01:00
}
/**
* This method is called in response to rmdir () calls on URL paths associated with the wrapper .
2008-04-18 10:42:11 +02:00
*
* It should attempt to remove the directory specified by path .
2008-02-18 07:52:07 +01:00
* 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 $url
* @ param int $options Possible values include STREAM_REPORT_ERRORS .
* @ return boolean TRUE on success or FALSE on failure .
*/
2008-10-01 20:01:45 +02:00
static function rmdir ( $url , $options )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-14 07:52:24 +02:00
$parent = dirname ( $path );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( ! ( $stat = self :: url_stat ( $path , 0 )) || $stat [ 'mime' ] != self :: DIR_MIME_TYPE ||
2008-04-14 07:52:24 +02:00
! egw_vfs :: check_access ( $parent , egw_vfs :: WRITABLE ))
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url );
2009-07-18 11:12:52 +02:00
$err_msg = __METHOD__ . " ( $url , $options ) " . ( ! $stat ? 'not found!' :
( $stat [ 'mime' ] != self :: DIR_MIME_TYPE ? 'not a directory!' : 'permission denied!' ));
if ( self :: LOG_LEVEL ) error_log ( $err_msg );
2010-02-07 07:19:56 +01:00
if ( ! ( $options & STREAM_REPORT_ERRORS ))
2008-02-18 07:52:07 +01:00
{
2009-07-18 11:12:52 +02:00
trigger_error ( $err_msg , E_USER_WARNING );
2008-02-18 07:52:07 +01:00
}
return false ; // no permission or file does not exist
}
$stmt = self :: $pdo -> prepare ( 'SELECT COUNT(*) FROM ' . self :: TABLE . ' WHERE fs_dir=?' );
$stmt -> execute ( array ( $stat [ 'ino' ]));
if ( $stmt -> fetchColumn ())
{
self :: _remove_password ( $url );
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $options ) dir is not empty! " );
2010-02-07 07:19:56 +01:00
if ( ! ( $options & STREAM_REPORT_ERRORS ))
2008-02-18 07:52:07 +01:00
{
trigger_error ( __METHOD__ . " (' $url ', $options ) dir is not empty! " , E_USER_WARNING );
}
return false ;
}
unset ( self :: $stat_cache [ $path ]);
2008-04-24 17:19:42 +02:00
unset ( $stmt ); // free statement object, on some installs a new prepare fails otherwise!
2008-02-18 07:52:07 +01:00
$stmt = self :: $pdo -> prepare ( 'DELETE FROM ' . self :: TABLE . ' WHERE fs_id=?' );
2008-10-03 10:30:40 +02:00
if (( $ret = $stmt -> execute ( array ( $stat [ 'ino' ]))))
2008-02-18 07:52:07 +01:00
{
2008-04-14 07:52:24 +02:00
self :: eacl ( $path , null , false , $stat [ 'ino' ]); // remove all (=false) evtl. existing extended acl for that dir
2008-10-05 19:07:36 +02:00
// delete props
unset ( $stmt );
$stmt = self :: $pdo -> prepare ( 'DELETE FROM ' . self :: PROPS_TABLE . ' WHERE fs_id=?' );
$stmt -> execute ( array ( $stat [ 'ino' ]));
2008-02-18 07:52:07 +01:00
}
return $ret ;
}
/**
* 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 ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
if ( ! ( $stat = self :: url_stat ( $path , STREAM_URL_STAT_QUIET )))
2008-02-18 07:52:07 +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
2008-02-26 09:51:42 +01:00
// file does not exist --> create an empty one
2008-04-14 07:52:24 +02:00
if ( ! ( $f = fopen ( self :: SCHEME . '://default' . $path , 'w' )) || ! fclose ( $f ))
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
2008-02-26 09:51:42 +01:00
{
2008-04-14 07:52:24 +02:00
return false ;
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
2008-02-26 09:51:42 +01:00
}
2008-04-14 07:52:24 +02:00
if ( is_null ( $time ))
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
2008-02-26 09:51:42 +01:00
{
2008-04-14 07:52:24 +02:00
return true ; // new (empty) file created with current mod time
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
2008-02-26 09:51:42 +01:00
}
2008-04-14 07:52:24 +02:00
$stat = self :: url_stat ( $path , 0 );
2008-02-18 07:52:07 +01:00
}
2009-03-27 20:10:16 +01:00
unset ( self :: $stat_cache [ $path ]);
2008-10-05 11:01:20 +02:00
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_modified=:fs_modified,fs_modifier=:fs_modifier WHERE fs_id=:fs_id' );
2008-02-18 07:52:07 +01:00
return $stmt -> execute ( array (
2009-03-30 13:17:30 +02:00
'fs_modified' => self :: _pdo_timestamp ( $time ? $time : time ()),
'fs_modifier' => egw_vfs :: $user ,
'fs_id' => $stat [ 'ino' ],
2008-02-18 07:52:07 +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
2008-02-26 09:51:42 +01:00
/**
* 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 ) " );
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
$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 ;
}
2008-10-26 08:37:37 +01:00
if ( $owner < 0 || $owner && ! $GLOBALS [ 'egw' ] -> accounts -> id2name ( $owner )) // not a user (0 == root)
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
2008-02-26 09:51:42 +01:00
{
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $url , $owner ) unknown (numeric) user id! " );
2008-10-26 08:37:37 +01:00
trigger_error ( __METHOD__ . " ( $url , $owner ) Unknown (numeric) user id! " , E_USER_WARNING );
//throw new Exception(__METHOD__."($url,$owner) Unknown (numeric) user id!");
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
2008-02-26 09:51:42 +01:00
return false ;
}
$stmt = self :: $pdo -> prepare ( 'UPDATE ' . self :: TABLE . ' SET fs_uid=:fs_uid WHERE fs_id=:fs_id' );
return $stmt -> execute ( array (
2009-03-30 13:17:30 +02:00
'fs_uid' => ( int ) $owner ,
'fs_id' => $stat [ 'ino' ],
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
2008-02-26 09:51:42 +01:00
));
}
/**
* 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 ) " );
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
$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 ;
}
2008-11-09 17:10:36 +01:00
if ( ! egw_vfs :: has_owner_rights ( $path , $stat ))
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
2008-02-26 09:51:42 +01:00
{
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 (
2009-03-30 13:17:30 +02:00
'fs_gid' => $owner ,
'fs_id' => $stat [ 'ino' ],
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
2008-02-26 09:51:42 +01:00
));
}
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
/**
* 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 ) " );
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
$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 ;
}
2008-11-09 17:10:36 +01:00
if ( ! egw_vfs :: has_owner_rights ( $path , $stat ))
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
2008-02-26 09:51:42 +01:00
{
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 (
2009-03-30 13:17:30 +02:00
'fs_mode' => (( int ) $mode ) & 0777 , // we dont store the file and dir bits, give int overflow!
'fs_id' => $stat [ 'ino' ],
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
2008-02-26 09:51:42 +01:00
));
}
2008-02-18 07:52:07 +01:00
/**
2008-04-18 10:42:11 +02:00
* This method is called immediately when your stream object is created for examining directory contents with opendir () .
*
2008-02-18 07:52:07 +01:00
* @ param string $path URL that was passed to opendir () and that this object is expected to explore .
* @ param $options
2008-04-18 10:42:11 +02:00
* @ return booelan
2008-02-18 07:52:07 +01:00
*/
function dir_opendir ( $url , $options )
{
$this -> opened_dir = null ;
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( ! ( $stat = self :: url_stat ( $url , 0 )) || // dir not found
$stat [ 'mime' ] != self :: DIR_MIME_TYPE || // no dir
2008-04-14 07:52:24 +02:00
! egw_vfs :: check_access ( $url , egw_vfs :: EXECUTABLE | egw_vfs :: READABLE , $stat )) // no access
2008-02-18 07:52:07 +01:00
{
self :: _remove_password ( $url );
$msg = $stat [ 'mime' ] != self :: DIR_MIME_TYPE ? " $url is no directory " : 'permission denied' ;
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " (' $url ', $options ) $msg ! " );
$this -> opened_dir = null ;
return false ;
}
2008-03-02 22:44:15 +01:00
$this -> opened_dir = array ();
2009-07-01 17:17:36 +02:00
$query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified' . self :: $extra_columns .
2010-02-07 07:19:56 +01:00
' FROM ' . self :: TABLE . ' WHERE fs_dir=? AND fs_active=' . self :: _pdo_boolean ( true ) .
" ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC " ;
2009-03-30 13:17:30 +02:00
//if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . " ( $url , $options ) " . ' */ ' . $query ;
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
$stmt = self :: $pdo -> prepare ( $query );
2008-02-18 07:52:07 +01:00
$stmt -> setFetchMode ( PDO :: FETCH_ASSOC );
if ( $stmt -> execute ( array ( $stat [ 'ino' ])))
{
foreach ( $stmt as $file )
{
$this -> opened_dir [] = $file [ 'fs_name' ];
2008-04-14 07:52:24 +02:00
self :: $stat_cache [ egw_vfs :: concat ( $path , $file [ 'fs_name' ])] = $file ;
2008-02-18 07:52:07 +01:00
}
}
2008-04-14 07:52:24 +02:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url , $options ): " . implode ( ', ' , $this -> opened_dir ));
2008-02-18 07:52:07 +01:00
reset ( $this -> opened_dir );
return true ;
}
/**
* This method is called in response to stat () calls on the URL paths associated with the wrapper .
2008-04-18 10:42:11 +02:00
*
* It should return as many elements in common with the system function as possible .
2008-02-18 07:52:07 +01:00
* Unknown or unavailable values should be set to a rational value ( usually 0 ) .
2008-04-18 10:42:11 +02:00
*
* If you plan to use your wrapper in a require_once you need to define stream_stat () .
2008-02-18 07:52:07 +01:00
* If you plan to allow any other tests like is_file () / is_dir (), you have to define url_stat () .
2008-04-18 10:42:11 +02:00
* stream_stat () must define the size of the file , or it will never be included .
2008-02-18 07:52:07 +01:00
* url_stat () must define mode , or is_file () / is_dir () / is_executable (), and any of those functions affected by clearstatcache () simply won ' t work .
2008-04-18 10:42:11 +02:00
* It ' s not documented , but directories must be a mode like 040777 ( octal ), and files a mode like 0100666.
* If you wish the file to be executable , use 7 s instead of 6 s .
* The last 3 digits are exactly the same thing as what you pass to chmod .
* 040000 defines a directory , and 0100000 defines a file .
2008-02-18 07:52:07 +01:00
*
* @ param string $path
* @ param int $flags holds additional flags set by the streams API . It can hold one or more of the following values OR ' d together :
2008-04-18 10:42:11 +02:00
* - STREAM_URL_STAT_LINK For resources with the ability to link to other resource ( such as an HTTP Location : forward ,
* or a filesystem symlink ) . This flag specified that only information about the link itself should be returned ,
* not the resource pointed to by the link .
2008-02-18 07:52:07 +01:00
* This flag is set in response to calls to lstat (), is_link (), or filetype () .
2008-04-18 10:42:11 +02:00
* - STREAM_URL_STAT_QUIET If this flag is set , your wrapper should not raise any errors . If this flag is not set ,
2008-02-18 07:52:07 +01:00
* 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 !
2008-04-14 07:52:24 +02:00
* @ param boolean $eacl_access = null allows extending classes to pass the value of their check_extended_acl () method ( no lsb ! )
2008-04-18 10:42:11 +02:00
* @ return array
2008-02-18 07:52:07 +01:00
*/
2008-04-14 07:52:24 +02:00
static function url_stat ( $url , $flags , $eacl_access = null )
2008-02-18 07:52:07 +01:00
{
2010-04-27 12:38:16 +02:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " (' $url ', $flags , $eacl_access ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
// webdav adds a trailing slash to dirs, which causes url_stat to NOT find the file otherwise
if ( $path != '/' && substr ( $path , - 1 ) == '/' )
{
$path = substr ( $path , 0 , - 1 );
}
2009-07-01 20:03:46 +02:00
if ( empty ( $path ))
{
return false ; // is invalid and gives sql error
}
2008-02-18 07:52:07 +01:00
// check if we already have the info from the last dir_open call, as the old vfs reads it anyway from the db
2009-04-07 21:42:46 +02:00
if ( self :: $stat_cache && isset ( self :: $stat_cache [ $path ]) && ( is_null ( $eacl_access ) || self :: $stat_cache [ $path ] !== false ))
2008-02-18 07:52:07 +01:00
{
2009-03-27 20:10:16 +01:00
return self :: $stat_cache [ $path ] ? self :: _vfsinfo2stat ( self :: $stat_cache [ $path ]) : false ;
2008-02-18 07:52:07 +01:00
}
if ( ! is_object ( self :: $pdo ))
{
self :: _pdo ();
}
2009-07-01 17:17:36 +02:00
$base_query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified' . self :: $extra_columns .
2010-02-07 07:19:56 +01:00
' FROM ' . self :: TABLE . ' WHERE fs_active=' . self :: _pdo_boolean ( true ) .
' AND fs_name' . self :: $case_sensitive_equal . '? AND fs_dir=' ;
2008-02-18 07:52:07 +01:00
$parts = explode ( '/' , $path );
2008-04-14 07:52:24 +02:00
// if we have extendes acl access to the url, we dont need and can NOT include the sql for the readable check
if ( is_null ( $eacl_access ))
{
$eacl_access = self :: check_extended_acl ( $path , egw_vfs :: READABLE ); // should be static::check_extended_acl, but no lsb!
}
2008-02-18 07:52:07 +01:00
foreach ( $parts as $n => $name )
{
if ( $n == 0 )
{
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
2008-02-26 09:51:42 +01:00
$query = ( int ) ( $path != '/' ); // / always has fs_id == 1, no need to query it ($path=='/' needs fs_dir=0!)
2008-02-18 07:52:07 +01:00
}
elseif ( $n < count ( $parts ) - 1 )
{
2009-05-01 21:12:25 +02:00
// MySQL 5.0 has a nesting limit for subqueries
// --> we replace the so far cumulated subqueries with their result
// no idea about the other DBMS, but this does NOT hurt ...
2009-05-04 18:47:21 +02:00
// setting the value to 7, after reports on the user list, thought MySQL 5.0.51 with MyISAM engine works up to 10
if ( $n > 1 && ! (( $n - 1 ) % 7 ) && ! ( $query = self :: $pdo -> query ( $query ) -> fetchColumn ()))
2009-05-01 21:12:25 +02:00
{
if ( self :: LOG_LEVEL > 1 )
{
self :: _remove_password ( $url );
error_log ( __METHOD__ . " (' $url ', $flags ) file or directory not found! " );
}
// we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)
return self :: $stat_cache [ $path ] = false ;
}
2010-02-07 07:19:56 +01:00
$query = 'SELECT fs_id FROM ' . self :: TABLE . ' WHERE fs_dir=(' . $query . ') AND fs_active=' .
self :: _pdo_boolean ( true ) . ' AND fs_name' . self :: $case_sensitive_equal . self :: $pdo -> quote ( $name );
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
2008-02-26 09:51:42 +01:00
2008-04-14 07:52:24 +02:00
// if we are not root AND have no extended acl access, we need to make sure the user has the right to tranverse all parent directories (read-rights)
if ( ! egw_vfs :: $is_root && ! $eacl_access )
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
2008-02-26 09:51:42 +01:00
{
2008-04-14 07:52:24 +02:00
if ( ! egw_vfs :: $user )
{
self :: _remove_password ( $url );
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " (' $url ', $flags ) permission denied, no user-id and not root! " );
return false ;
}
2008-02-29 08:23:14 +01:00
$query .= ' AND ' . self :: _sql_readable ();
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
2008-02-26 09:51:42 +01:00
}
2008-02-18 07:52:07 +01:00
}
else
{
2009-03-27 20:10:16 +01:00
$query = str_replace ( 'fs_name' . self :: $case_sensitive_equal . '?' , 'fs_name' . self :: $case_sensitive_equal . self :: $pdo -> quote ( $name ), $base_query ) . '(' . $query . ')' ;
2008-02-18 07:52:07 +01:00
}
}
2009-03-30 13:17:30 +02:00
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . " ( $url , $flags , $eacl_access ) " . ' */ ' . $query ;
//if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( ! ( $result = self :: $pdo -> query ( $query )) || ! ( $info = $result -> fetch ( PDO :: FETCH_ASSOC )))
{
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 1 )
{
self :: _remove_password ( $url );
error_log ( __METHOD__ . " (' $url ', $flags ) file or directory not found! " );
}
// we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)
return self :: $stat_cache [ $path ] = false ;
2008-02-18 07:52:07 +01:00
}
self :: $stat_cache [ $path ] = $info ;
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url , $flags )= " . array2string ( $info ));
2008-02-18 07:52:07 +01:00
return self :: _vfsinfo2stat ( $info );
}
2008-04-18 10:42:11 +02: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
2008-02-26 09:51:42 +01:00
/**
* Return readable check as sql ( to be AND ' ed into the query ), only use if ! egw_vfs :: $is_root
*
* @ return string
*/
2010-02-07 07:19:56 +01:00
protected function _sql_readable ()
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
2008-02-26 09:51:42 +01:00
{
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
}
2009-04-15 13:21:44 +02:00
// using octal numbers with mysql leads to funny results (select 384 & 0400 --> 384 not 256=0400)
// 256 = 0400, 32 = 040
$sql_read_acl = '((fs_mode & 4)=4 OR (fs_mode & 256)=256 AND fs_uid=' . ( int ) egw_vfs :: $user .
' OR (fs_mode & 32)=32 AND fs_gid IN(' . implode ( ',' , $memberships ) . '))' ;
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
2008-02-26 09:51:42 +01:00
}
return $sql_read_acl ;
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* This method is called in response to readdir () .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* It should return a string representing the next filename in the location opened by dir_opendir () .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ return string
*/
function dir_readdir ( )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( ) " );
if ( ! is_array ( $this -> opened_dir )) return false ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$file = current ( $this -> opened_dir ); next ( $this -> opened_dir );
2008-03-02 22:44:15 +01:00
return $file ;
2008-02-18 07:52:07 +01:00
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* This method is called in response to rewinddir () .
2008-04-18 10:42:11 +02:00
*
* It should reset the output generated by dir_readdir () . i . e .:
2008-02-18 07:52:07 +01:00
* The next call to dir_readdir () should return the first entry in the location returned by dir_opendir () .
2008-04-18 10:42:11 +02:00
*
2008-02-18 07:52:07 +01:00
* @ return boolean
*/
2008-04-18 10:42:11 +02:00
function dir_rewinddir ( )
2008-02-18 07:52:07 +01:00
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( ! is_array ( $this -> opened_dir )) return false ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
reset ( $this -> opened_dir );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
return true ;
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* This method is called in response to closedir () .
2008-04-18 10:42:11 +02:00
*
* You should release any resources which were locked or allocated during the opening and use of the directory stream .
*
2008-02-18 07:52:07 +01:00
* @ return boolean
*/
function dir_closedir ( )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( ) " );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( ! is_array ( $this -> opened_dir )) return false ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
$this -> opened_dir = null ;
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
return true ;
}
2008-04-18 10:42:11 +02:00
2009-03-19 21:12:35 +01:00
/**
* This method is called in response to readlink () .
*
2009-03-27 20:10:16 +01:00
* The readlink value is read by url_stat or dir_opendir and therefore cached in the stat - cache .
*
2009-03-19 21:12:35 +01:00
* @ param string $url
* @ return string | boolean content of the symlink or false if $url is no symlink ( or not found )
*/
2009-03-27 20:10:16 +01:00
static function readlink ( $path )
2009-03-19 21:12:35 +01:00
{
2009-03-27 20:10:16 +01:00
$link = ! ( $lstat = self :: url_stat ( $path , STREAM_URL_STAT_LINK )) || is_null ( $lstat [ 'readlink' ]) ? false : $lstat [ 'readlink' ];
2009-03-19 21:12:35 +01:00
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " (' $path ') = $link " );
2009-03-19 21:12:35 +01:00
2009-03-27 20:10:16 +01:00
return $link ;
2009-03-19 21:12:35 +01:00
}
/**
* Method called for symlink ()
*
* @ param string $target
* @ param string $link
* @ return boolean true on success false on error
*/
static function symlink ( $target , $link )
{
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " (' $target ',' $link ') " );
if ( self :: url_stat ( $link , 0 ) || ! ( $dir = dirname ( $link )) ||
! egw_vfs :: check_access ( $dir , egw_vfs :: WRITABLE , $dir_stat = self :: url_stat ( $dir , 0 )))
{
if ( self :: LOG_LEVEL > 0 ) error_log ( __METHOD__ . " (' $target ',' $link ') returning false! (!stat(' $link ') || !is_writable(' $dir ')) " );
return false ; // $link already exists or parent dir does not
}
2009-05-12 09:21:57 +02:00
$query = 'INSERT INTO ' . self :: TABLE . ' (fs_name,fs_dir,fs_mode,fs_uid,fs_gid,fs_created,fs_modified,fs_creator,fs_mime,fs_size,fs_link' .
') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_link)' ;
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . ': ' . __LINE__ . ' */ ' . $query ;
$stmt = self :: $pdo -> prepare ( $query );
2009-03-30 13:17:30 +02:00
unset ( self :: $stat_cache [ $link ]);
return !! $stmt -> execute ( array (
2009-04-09 15:24:34 +02:00
'fs_name' => egw_vfs :: basename ( $link ),
2009-03-19 21:12:35 +01:00
'fs_dir' => $dir_stat [ 'ino' ],
'fs_mode' => ( $dir_stat [ 'mode' ] & 0666 ),
'fs_uid' => $dir_stat [ 'uid' ] ? $dir_stat [ 'uid' ] : egw_vfs :: $user ,
'fs_gid' => $dir_stat [ 'gid' ],
'fs_created' => self :: _pdo_timestamp ( time ()),
'fs_modified' => self :: _pdo_timestamp ( time ()),
'fs_creator' => egw_vfs :: $user ,
'fs_mime' => self :: SYMLINK_MIME_TYPE ,
'fs_size' => bytes ( $target ),
2009-05-12 09:21:57 +02:00
'fs_link' => $target ,
2009-03-30 13:17:30 +02:00
));
2009-03-19 21:12:35 +01:00
}
2008-04-14 07:52:24 +02:00
private static $extended_acl ;
/**
* Check if extendes ACL ( stored in eGW ' s ACL table ) grants access
2008-04-18 10:42:11 +02:00
*
2008-04-14 07:52:24 +02:00
* The extended ACL is inherited , so it ' s valid for all subdirs and the included files !
* The used algorithm break on the first match . It could be used , to disallow further access .
*
* @ param string $url url to check
* @ param int $check mode to check : one or more or ' ed together of : 4 = read , 2 = write , 1 = executable
* @ return boolean
*/
static function check_extended_acl ( $url , $check )
{
$url_path = parse_url ( $url , PHP_URL_PATH );
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
if ( is_null ( self :: $extended_acl ))
{
self :: _read_extended_acl ();
}
$access = false ;
foreach ( self :: $extended_acl as $path => $rights )
{
if ( $path == $url_path || substr ( $url_path , 0 , strlen ( $path ) + 1 ) == $path . '/' )
{
$access = ( $rights & $check ) == $check ;
break ;
}
}
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $url , $check ) " . ( $access ? " access granted by $path = $rights " : 'no access!!!' ));
2008-04-14 07:52:24 +02:00
return $access ;
}
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
/**
* Read the extended acl via acl :: get_grants ( 'sqlfs' )
*
*/
2010-02-07 07:19:56 +01:00
static protected function _read_extended_acl ()
2008-04-14 07:52:24 +02:00
{
if (( self :: $extended_acl = $GLOBALS [ 'egw' ] -> session -> appsession ( 'extended_acl' , self :: EACL_APPNAME )) != false )
{
return ; // ext. ACL read from session.
}
self :: $extended_acl = array ();
if (( $rights = $GLOBALS [ 'egw' ] -> acl -> get_all_location_rights ( egw_vfs :: $user , self :: EACL_APPNAME )))
{
$pathes = self :: id2path ( array_keys ( $rights ));
}
foreach ( $rights as $fs_id => $right )
{
$path = $pathes [ $fs_id ];
if ( isset ( $path ))
{
self :: $extended_acl [ $path ] = ( int ) $right ;
}
}
// sort by length descending, to allow more specific pathes to have precedence
uksort ( self :: $extended_acl , create_function ( '$a,$b' , 'return strlen($b)-strlen($a);' ));
$GLOBALS [ 'egw' ] -> session -> appsession ( 'extended_acl' , self :: EACL_APPNAME , self :: $extended_acl );
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . '() ' . array2string ( self :: $extended_acl ));
2008-04-14 07:52:24 +02:00
}
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
/**
* Appname used with the acl class to store the extended acl
*/
const EACL_APPNAME = 'sqlfs' ;
/**
* Set or delete extended acl for a given path and owner ( or delete them if is_null ( $rights )
2008-04-18 10:42:11 +02:00
*
2008-04-14 07:52:24 +02:00
* Only root , the owner of the path or an eGW admin ( only if there 's no owner but a group) are allowd to set eACL' s !
*
* @ param string $path string with path
* @ param int $rights = null rights to set , or null to delete the entry
* @ param int / boolean $owner = null owner for whom to set the rights , null for the current user , or false to delete all rights for $path
* @ param int $fs_id = null fs_id to use , to not query it again ( eg . because it ' s already deleted )
* @ return boolean true if acl is set / deleted , false on error
*/
static function eacl ( $path , $rights = null , $owner = null , $fs_id = null )
{
if ( $path [ 0 ] != '/' )
{
$path = parse_url ( $path , PHP_URL_PATH );
}
if ( is_null ( $fs_id ))
{
if ( ! ( $stat = self :: url_stat ( $path , 0 )))
{
2008-11-15 08:40:25 +01:00
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $path , $rights , $owner , $fs_id ) no such file or directory! " );
2008-04-14 07:52:24 +02:00
return false ; // $path not found
}
if ( ! egw_vfs :: has_owner_rights ( $path , $stat )) // not group dir and user is eGW admin
{
2008-11-15 08:40:25 +01:00
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( $path , $rights , $owner , $fs_id ) permission denied! " );
2008-04-14 07:52:24 +02:00
return false ; // permission denied
}
$fs_id = $stat [ 'ino' ];
}
if ( is_null ( $owner ))
{
$owner = egw_vfs :: $user ;
}
if ( is_null ( $rights ) || $owner === false )
{
// delete eacl
if ( is_null ( $owner ) || $owner == egw_vfs :: $user )
{
unset ( self :: $extended_acl [ $path ]);
}
$ret = $GLOBALS [ 'egw' ] -> acl -> delete_repository ( self :: EACL_APPNAME , $fs_id ,( int ) $owner );
}
else
{
2008-04-18 10:42:11 +02:00
if ( isset ( self :: $extended_acl ) && ( $owner == egw_vfs :: $user ||
2008-04-14 07:52:24 +02:00
$owner < 0 && egw_vfs :: $user && in_array ( $owner , $GLOBALS [ 'egw' ] -> accounts -> memberships ( egw_vfs :: $user , true ))))
{
// set rights for this class, if applicable
self :: $extended_acl [ $path ] = $rights ;
}
$ret = $GLOBALS [ 'egw' ] -> acl -> add_repository ( self :: EACL_APPNAME , $fs_id , $owner , $rights );
}
if ( $ret )
{
2008-04-18 10:42:11 +02:00
$GLOBALS [ 'egw' ] -> session -> appsession ( 'extended_acl' , self :: EACL_APPNAME , self :: $extended_acl );
2008-04-14 07:52:24 +02:00
}
2008-11-15 08:40:25 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $path , $rights , $owner , $fs_id )= " . ( int ) $ret );
2008-04-14 07:52:24 +02:00
return $ret ;
}
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
/**
* Get all ext . ACL set for a path
2008-04-18 10:42:11 +02:00
*
2008-04-14 07:52:24 +02:00
* Calls itself recursive , to get the parent directories
*
* @ param string $path
* @ return array / boolean array with array ( 'path' => $path , 'owner' => $owner , 'rights' => $rights ) or false if $path not found
*/
function get_eacl ( $path )
{
if ( ! ( $stat = self :: url_stat ( $path , STREAM_URL_STAT_QUIET )))
{
return false ; // not found
}
$eacls = array ();
foreach ( $GLOBALS [ 'egw' ] -> acl -> get_all_rights ( $stat [ 'ino' ], self :: EACL_APPNAME ) as $owner => $rights )
{
$eacls [] = array (
'path' => $path ,
'owner' => $owner ,
'rights' => $rights ,
'ino' => $stat [ 'ino' ],
);
}
if (( $path = egw_vfs :: dirname ( $path )))
{
return array_merge ( self :: get_eacl ( $path ), $eacls );
}
// sort by length descending, to show precedence
usort ( $eacls , create_function ( '$a,$b' , 'return strlen($b["path"])-strlen($a["path"]);' ));
return $eacls ;
}
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
/**
* Return the path of given fs_id ( s )
2008-04-18 10:42:11 +02:00
*
2008-10-05 19:07:36 +02:00
* Searches the stat_cache first and then the db .
2008-04-14 07:52:24 +02:00
* Calls itself recursive to to determine the path of the parent / directory
*
* @ param int / array $fs_ids integer fs_id or array of them
* @ return string / array path or array or pathes indexed by fs_id
*/
static function id2path ( $fs_ids )
{
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . '(' . array2string ( $fs_id ) . ')' );
2008-04-14 07:52:24 +02:00
$ids = ( array ) $fs_ids ;
2008-10-05 19:07:36 +02:00
$pathes = array ();
// first check our stat-cache for the ids
foreach ( self :: $stat_cache as $path => $stat )
{
if (( $key = array_search ( $stat [ 'fs_id' ], $ids )) !== false )
{
$pathes [ $stat [ 'fs_id' ]] = $path ;
unset ( $ids [ $key ]);
if ( ! $ids )
{
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . '(' . array2string ( $fs_ids ) . ')=' . array2string ( $pathes ) . ' *from stat_cache*' );
2008-10-05 19:07:36 +02:00
return is_array ( $fs_ids ) ? $pathes : array_shift ( $pathes );
}
}
}
// now search via the database
2008-04-14 07:52:24 +02:00
if ( count ( $ids ) > 1 ) array_map ( create_function ( '&$v' , '$v = (int)$v;' ), $ids );
$query = 'SELECT fs_id,fs_dir,fs_name FROM ' . self :: TABLE . ' WHERE fs_id' .
( count ( $ids ) == 1 ? '=' . ( int ) $ids [ 0 ] : ' IN (' . implode ( ',' , $ids ) . ')' );
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . ': ' . __LINE__ . ' */ ' . $query ;
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
if ( ! is_object ( self :: $pdo ))
{
self :: _pdo ();
}
$stmt = self :: $pdo -> prepare ( $query );
$stmt -> setFetchMode ( PDO :: FETCH_ASSOC );
if ( ! $stmt -> execute ())
{
return false ; // not found
}
$parents = array ();
foreach ( $stmt as $row )
{
if ( $row [ 'fs_dir' ] > 1 && ! in_array ( $row [ 'fs_dir' ], $parents ))
{
$parents [] = $row [ 'fs_dir' ];
}
$rows [ $row [ 'fs_id' ]] = $row ;
}
unset ( $stmt );
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
if ( $parents && ! ( $parents = self :: id2path ( $parents )))
{
return false ; // parent not found, should never happen ...
}
2009-01-29 19:58:52 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " trying foreach with: " . print_r ( $rows , true ) . " # " );
2009-04-02 12:18:05 +02:00
foreach (( array ) $rows as $fs_id => $row )
2008-04-14 07:52:24 +02:00
{
$parent = $row [ 'fs_dir' ] > 1 ? $parents [ $row [ 'fs_dir' ]] : '' ;
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
$pathes [ $fs_id ] = $parent . '/' . $row [ 'fs_name' ];
}
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . '(' . array2string ( $fs_ids ) . ')=' . array2string ( $pathes ));
2008-04-14 07:52:24 +02:00
return is_array ( $fs_ids ) ? $pathes : array_shift ( $pathes );
}
2008-02-18 07:52:07 +01:00
/**
* Convert a sqlfs - file - info into a stat array
*
* @ param array $info
* @ return array
*/
2010-02-07 07:19:56 +01:00
static protected function _vfsinfo2stat ( $info )
2008-02-18 07:52:07 +01:00
{
$stat = array (
'ino' => $info [ 'fs_id' ],
'name' => $info [ 'fs_name' ],
2008-04-18 10:42:11 +02:00
'mode' => $info [ 'fs_mode' ] |
2009-03-19 21:12:35 +01:00
( $info [ 'fs_mime' ] == self :: DIR_MIME_TYPE ? self :: MODE_DIR :
( $info [ 'fs_mime' ] == self :: SYMLINK_MIME_TYPE ? self :: MODE_LINK : self :: MODE_FILE )), // required by the stream wrapper
2008-02-18 07:52:07 +01:00
'size' => $info [ 'fs_size' ],
'uid' => $info [ 'fs_uid' ],
'gid' => $info [ 'fs_gid' ],
'mtime' => strtotime ( $info [ 'fs_modified' ]),
'ctime' => strtotime ( $info [ 'fs_created' ]),
'nlink' => $info [ 'fs_mime' ] == self :: DIR_MIME_TYPE ? 2 : 1 ,
2009-03-27 20:10:16 +01:00
// eGW addition to return some extra values
2008-02-18 07:52:07 +01:00
'mime' => $info [ 'fs_mime' ],
2009-05-12 09:21:57 +02:00
'readlink' => $info [ 'fs_link' ],
2008-02-18 07:52:07 +01:00
);
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( $info[name] ) = " . array2string ( $stat ));
2008-02-18 07:52:07 +01:00
return $stat ;
}
2008-04-18 10:42:11 +02:00
2008-10-05 19:07:36 +02:00
private static $pdo_type ;
2009-03-27 20:10:16 +01:00
/**
* Case sensitive comparison operator , for mysql we use ' COLLATE utf8_bin ='
*
* @ var string
*/
public static $case_sensitive_equal = '=' ;
2008-10-05 19:07:36 +02:00
2008-02-18 07:52:07 +01:00
/**
* Create pdo object / connection , as long as pdo is not generally used in eGW
*
* @ return PDO
*/
2010-02-07 07:19:56 +01:00
static protected function _pdo ()
2008-02-18 07:52:07 +01:00
{
2008-04-14 07:52:24 +02:00
$egw_db = isset ( $GLOBALS [ 'egw_setup' ]) ? $GLOBALS [ 'egw_setup' ] -> db : $GLOBALS [ 'egw' ] -> db ;
2008-04-18 10:42:11 +02:00
2008-04-14 07:52:24 +02:00
switch ( $egw_db -> Type )
2008-02-18 07:52:07 +01:00
{
2008-10-01 20:01:45 +02:00
case 'mysqli' :
2008-04-18 10:42:11 +02:00
case 'mysqlt' :
2009-03-27 20:10:16 +01:00
case 'mysql' :
2009-04-16 09:38:16 +02:00
self :: $case_sensitive_equal = '= BINARY ' ;
2008-10-05 19:07:36 +02:00
self :: $pdo_type = 'mysql' ;
2008-04-18 10:42:11 +02:00
break ;
2008-02-18 07:52:07 +01:00
default :
2008-10-05 19:07:36 +02:00
self :: $pdo_type = $egw_db -> Type ;
2008-02-18 07:52:07 +01:00
break ;
}
2008-04-18 11:08:38 +02:00
switch ( $type )
{
default :
2008-10-05 19:07:36 +02:00
$dsn = self :: $pdo_type . ':host=' . $egw_db -> Host . ';port=' . $egw_db -> Port . ';dbname=' . $egw_db -> Database ;
2008-04-18 11:08:38 +02:00
break ;
}
2008-08-17 07:41:49 +02:00
// check once if pdo extension and DB specific driver is loaded or can be loaded
static $pdo_available ;
if ( is_null ( $pdo_available ))
{
2008-10-05 19:07:36 +02:00
foreach ( array ( 'pdo' , 'pdo_' . self :: $pdo_type ) as $ext )
2008-08-17 07:41:49 +02:00
{
2009-04-20 14:43:44 +02:00
check_load_extension ( $ext , true ); // true = throw Exception
2008-08-17 07:41:49 +02:00
}
$pdo_available = true ;
}
2008-04-18 11:08:38 +02:00
try {
2009-07-01 17:17:36 +02:00
self :: $pdo = new PDO ( $dsn , $egw_db -> User , $egw_db -> Password , array (
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
));
2008-04-18 11:08:38 +02:00
} catch ( Exception $e ) {
2008-08-17 07:41:49 +02:00
2008-04-18 11:08:38 +02:00
// Exception reveals password, so we ignore the exception and connect again without pw, to get the right exception without pw
self :: $pdo = new PDO ( $dsn , $egw_db -> User , '$egw_db->Password' );
}
2008-03-02 22:44:15 +01:00
// set client charset of the connection
2010-02-07 07:19:56 +01:00
$charset = translation :: charset ();
2008-10-05 19:07:36 +02:00
switch ( self :: $pdo_type )
2008-03-02 22:44:15 +01:00
{
case 'mysql' :
2008-04-14 07:52:24 +02:00
if ( isset ( $egw_db -> Link_ID -> charset2mysql [ $charset ])) $charset = $egw_db -> Link_ID -> charset2mysql [ $charset ];
2008-03-02 22:44:15 +01:00
// fall throught
case 'pgsql' :
$query = " SET NAMES ' $charset ' " ;
break ;
}
if ( $query )
{
self :: $pdo -> exec ( $query );
}
return self :: $pdo ;
2008-02-18 07:52:07 +01:00
}
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
/**
* Just a little abstration ' til I know how to organise stuff like that with PDO
*
* @ param mixed $time
* @ return string Y - m - d H : i : s
*/
2010-02-07 07:19:56 +01:00
static protected function _pdo_timestamp ( $time )
2008-02-18 07:52:07 +01:00
{
if ( is_numeric ( $time ))
{
$time = date ( 'Y-m-d H:i:s' , $time );
}
return $time ;
}
2010-03-17 10:11:28 +01:00
2010-02-07 07:19:56 +01:00
/**
* Just a little abstration ' til I know how to organise stuff like that with PDO
*
* @ param boolean $val
* @ return string '1' or '0' for mysql , 'true' or 'false' for everyone else
*/
static protected function _pdo_boolean ( $val )
{
if ( self :: $pdo_type == 'mysql' )
{
return $val ? '1' : '0' ;
}
return $val ? 'true' : 'false' ;
}
2008-04-18 10:42:11 +02:00
2008-10-03 14:18:19 +02:00
/**
* Maximum value for a single hash element ( should be 10 ^ N ) : 10 , 100 ( default ), 1000 , ...
*
* DONT change this value , once you have files stored , they will no longer be found !
*/
const HASH_MAX = 100 ;
2008-02-18 07:52:07 +01:00
/**
* Return the path of the stored content of a file if $this -> operation == self :: STORE2FS
*
2008-10-03 14:18:19 +02:00
* To limit the number of files stored in one directory , we create a hash from the fs_id :
2008-10-21 09:08:12 +02:00
* 1 --> / 00 / 1
* 34 --> / 00 / 34
2008-10-03 14:18:19 +02:00
* 123 --> / 01 / 123
* 4567 --> / 45 / 4567
* 99999 --> / 09 / 99 / 99999
* --> so one directory contains maximum 2 * HASH_MAY entries ( HASH_MAX dirs + HASH_MAX files )
* @ param int $id id of the file
2008-02-18 07:52:07 +01:00
* @ return string
*/
2008-10-03 14:18:19 +02:00
static function _fs_path ( $id )
2008-02-18 07:52:07 +01:00
{
2008-10-03 14:18:19 +02:00
if ( ! is_numeric ( $id ))
{
throw new egw_exception_wrong_parameter ( __METHOD__ . " (id= $id ) id has to be an integer! " );
}
2008-10-21 09:08:12 +02:00
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'files_dir' ]))
2008-04-21 17:37:11 +02:00
{
if ( is_object ( $GLOBALS [ 'egw_setup' ] -> db )) // if we run under setup, query the db for the files dir
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'files_dir' ] = $GLOBALS [ 'egw_setup' ] -> db -> select ( 'egw_config' , 'config_value' , array (
'config_name' => 'files_dir' ,
'config_app' => 'phpgwapi' ,
2009-03-27 20:10:16 +01:00
), __LINE__ , __FILE__ ) -> fetchColumn ();
2008-04-21 17:37:11 +02:00
}
2008-10-21 09:08:12 +02:00
}
if ( ! $GLOBALS [ 'egw_info' ][ 'server' ][ 'files_dir' ])
{
throw new egw_exception_assertion_failed ( " \$ GLOBALS['egw_info']['server']['files_dir'] not set! " );
2008-04-21 17:37:11 +02:00
}
2008-10-03 14:18:19 +02:00
$hash = array ();
for ( $n = $id ; $n = ( int ) ( $n / self :: HASH_MAX ); )
{
$hash [] = sprintf ( '%02d' , $n % self :: HASH_MAX );
}
2008-10-21 09:08:12 +02:00
if ( ! $hash ) $hash [] = '00' ; // we need at least one directory, to not conflict with the dir-names
2008-10-03 14:18:19 +02:00
array_unshift ( $hash , $id );
$path = '/sqlfs/' . implode ( '/' , array_reverse ( $hash ));
//error_log(__METHOD__."($id) = '$path'");
2008-02-18 07:52:07 +01:00
return $GLOBALS [ 'egw_info' ][ 'server' ][ 'files_dir' ] . $path ;
}
/**
* Replace the password of an url with '...' for error messages
*
* @ param string & $url
*/
2010-02-07 07:19:56 +01:00
static protected function _remove_password ( & $url )
2008-02-18 07:52:07 +01:00
{
$parts = parse_url ( $url );
2008-04-18 10:42:11 +02:00
2008-02-18 07:52:07 +01:00
if ( $parts [ 'pass' ] || $parts [ 'scheme' ])
{
$url = $parts [ 'scheme' ] . '://' . ( $parts [ 'user' ] ? $parts [ 'user' ] . ( $parts [ 'pass' ] ? ':...' : '' ) . '@' : '' ) .
$parts [ 'host' ] . $parts [ 'path' ];
}
}
2008-10-01 20:01:45 +02:00
/**
* Get storage mode from url ( get parameter 'storage' , eg . ? storage = db )
*
* @ param string | array $url complete url or array of url - parts from parse_url
* @ return int self :: STORE2FS or self :: STORE2DB
*/
static function url2operation ( $url )
{
$operation = self :: DEFAULT_OPERATION ;
if ( strpos ( is_array ( $url ) ? $url [ 'query' ] : $url , 'storage=' ) !== false )
{
parse_str ( is_array ( $url ) ? $url [ 'query' ] : parse_url ( $url , PHP_URL_QUERY ), $query );
switch ( $query [ 'storage' ])
{
case 'db' :
$operation = self :: STORE2DB ;
break ;
case 'fs' :
default :
$operation = self :: STORE2FS ;
break ;
}
}
//error_log(__METHOD__."('$url') = $operation (1=DB, 2=FS)");
return $operation ;
}
2008-10-05 19:07:36 +02:00
/**
* Store properties for a single ressource ( file or dir )
*
* @ param string | int $path string with path or integer fs_id
2008-10-05 21:01:49 +02:00
* @ param array $props array or array with values for keys 'name' , 'ns' , 'val' ( null to delete the prop )
2008-10-05 19:07:36 +02:00
* @ return boolean true if props are updated , false otherwise ( eg . ressource not found )
*/
2010-02-15 07:02:36 +01:00
static function proppatch ( $path , array $props )
2008-10-05 19:07:36 +02:00
{
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) error_log ( __METHOD__ . " ( " . array2string ( $path ) . ',' . array2string ( $props ));
2008-10-05 19:07:36 +02:00
if ( ! is_numeric ( $path ))
{
if ( ! ( $stat = self :: url_stat ( $path , 0 )))
{
return false ;
}
$id = $stat [ 'ino' ];
}
elseif ( ! ( $path = self :: id2path ( $id = $path )))
{
return false ;
}
if ( ! egw_vfs :: check_access ( $path , EGW_ACL_EDIT , $stat ))
{
return false ; // permission denied
}
foreach ( $props as & $prop )
{
if ( ! isset ( $prop [ 'ns' ])) $prop [ 'ns' ] = egw_vfs :: DEFAULT_PROP_NAMESPACE ;
2008-10-05 21:01:49 +02:00
if ( ! isset ( $prop [ 'val' ]) || self :: $pdo_type != 'mysql' ) // for non mysql, we have to delete the prop anyway, as there's no REPLACE!
2008-10-05 19:07:36 +02:00
{
if ( ! isset ( $del_stmt ))
{
$del_stmt = self :: $pdo -> prepare ( 'DELETE FROM ' . self :: PROPS_TABLE . ' WHERE fs_id=:fs_id AND prop_namespace=:prop_namespace AND prop_name=:prop_name' );
}
2009-03-30 13:17:30 +02:00
$del_stmt -> execute ( array (
'fs_id' => $id ,
'prop_namespace' => $prop [ 'ns' ],
'prop_name' => $prop [ 'name' ],
));
2008-10-05 19:07:36 +02:00
}
2008-10-05 21:01:49 +02:00
if ( isset ( $prop [ 'val' ]))
2008-10-05 19:07:36 +02:00
{
if ( ! isset ( $ins_stmt ))
{
$ins_stmt = self :: $pdo -> prepare (( self :: $pdo_type == 'mysql' ? 'REPLACE' : 'INSERT' ) .
' INTO ' . self :: PROPS_TABLE . ' (fs_id,prop_namespace,prop_name,prop_value) VALUES (:fs_id,:prop_namespace,:prop_name,:prop_value)' );
}
2009-03-30 13:17:30 +02:00
if ( ! $ins_stmt -> execute ( array (
'fs_id' => $id ,
'prop_namespace' => $prop [ 'ns' ],
'prop_name' => $prop [ 'name' ],
'prop_value' => $prop [ 'val' ],
)))
2008-10-05 19:07:36 +02:00
{
return false ;
}
}
}
return true ;
}
/**
* Read properties for a ressource ( file , dir or all files of a dir )
*
* @ param array | string | int $path_ids ( array of ) string with path or integer fs_id
* @ param string $ns = 'http://egroupware.org/' namespace if propfind should be limited to a single one , use null for all
* @ return array | boolean false on error ( $path_ids does not exist ), array with props ( values for keys 'name' , 'ns' , 'value' ), or
* fs_id / path => array of props for $depth == 1 or is_array ( $path_ids )
*/
static function propfind ( $path_ids , $ns = egw_vfs :: DEFAULT_PROP_NAMESPACE )
{
$ids = is_array ( $path_ids ) ? $path_ids : array ( $path_ids );
foreach ( $ids as & $id )
{
if ( ! is_numeric ( $id ))
{
if ( ! ( $stat = self :: url_stat ( $id , 0 )))
{
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL ) error_log ( __METHOD__ . " ( " . array2string ( $path_ids ) . " , $ns ) path ' $id ' not found! " );
2008-10-05 19:07:36 +02:00
return false ;
}
$id = $stat [ 'ino' ];
}
}
2009-06-10 12:31:28 +02:00
if ( count ( $ids ) >= 1 ) array_map ( create_function ( '&$v' , '$v = (int)$v;' ), $ids );
2008-10-05 19:07:36 +02:00
$query = 'SELECT * FROM ' . self :: PROPS_TABLE . ' WHERE (fs_id' .
2009-06-10 12:31:28 +02:00
( count ( $ids ) == 1 ? '=' . ( int ) implode ( '' , $ids ) : ' IN (' . implode ( ',' , $ids ) . ')' ) . ')' .
2008-10-05 19:07:36 +02:00
( ! is_null ( $ns ) ? ' AND prop_namespace=?' : '' );
2009-03-27 20:10:16 +01:00
if ( self :: LOG_LEVEL > 2 ) $query = '/* ' . __METHOD__ . ': ' . __LINE__ . ' */ ' . $query ;
2008-10-05 19:07:36 +02:00
$stmt = self :: $pdo -> prepare ( $query );
$stmt -> setFetchMode ( PDO :: FETCH_ASSOC );
$stmt -> execute ( ! is_null ( $ns ) ? array ( $ns ) : array ());
$props = array ();
foreach ( $stmt as $row )
{
$props [ $row [ 'fs_id' ]][] = array (
2008-10-05 21:01:49 +02:00
'val' => $row [ 'prop_value' ],
'name' => $row [ 'prop_name' ],
'ns' => $row [ 'prop_namespace' ],
2008-10-05 19:07:36 +02:00
);
}
if ( ! is_array ( $path_ids ))
{
$props = $props [ $row [ 'fs_id' ]];
}
elseif ( $props && isset ( $stat )) // need to map fs_id's to pathes
{
foreach ( self :: id2path ( array_keys ( $props )) as $id => $path )
{
$props [ $path ] =& $props [ $id ];
unset ( $props [ $id ]);
}
}
2008-11-13 07:35:18 +01:00
if ( self :: LOG_LEVEL > 1 ) foreach (( array ) $props as $k => $v ) error_log ( __METHOD__ . " ( $path_ids , $ns ) $k => " . array2string ( $v ));
2008-10-05 19:07:36 +02:00
return $props ;
}
2010-03-17 10:11:28 +01:00
/**
* Migrate SQLFS content from DB to filesystem
*
* @ param boolean $debug true to echo a message for each copied file
*/
static function migrate_db2fs ( $debug = false )
{
if ( ! is_object ( self :: $pdo ))
{
self :: _pdo ();
}
$query = 'SELECT fs_id,fs_name,fs_size,fs_content' .
' FROM ' . self :: TABLE . ' WHERE fs_content IS NOT NULL' ;
$stmt = self :: $pdo -> prepare ( $query );
$stmt -> bindColumn ( 1 , $fs_id );
$stmt -> bindColumn ( 2 , $fs_name );
$stmt -> bindColumn ( 3 , $fs_size );
$stmt -> bindColumn ( 4 , $fs_content , PDO :: PARAM_LOB );
if ( $stmt -> execute ())
{
foreach ( $stmt as $row )
{
// hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913)
// PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-(
if ( is_string ( $fs_content ))
{
$name = md5 ( $fs_name . $fs_id );
$GLOBALS [ $name ] =& $fs_content ;
require_once ( EGW_API_INC . '/class.global_stream_wrapper.inc.php' );
$content = fopen ( 'global://' . $name , 'r' );
if ( ! $content ) echo " fopen('global:// $name ','w' failed, strlen( \$ GLOBALS[' $name '])= " . strlen ( $GLOBALS [ $name ]) . " , \$ GLOBALS[' $name ']= " . substr ( $GLOBALS [ '$name' ], 0 , 100 ) . " ... \n " ;
unset ( $GLOBALS [ $name ]); // unset it, so it does not use up memory, once the stream is closed
}
else
{
$content = $fs_content ;
}
if ( ! is_resource ( $content ))
{
throw new egw_exception_assertion_failed ( __METHOD__ . " (): fs_id= $fs_id ( $fs_name , $fs_size bytes) content is NO resource! " . array2string ( $content ));
}
$filename = self :: _fs_path ( $fs_id );
if ( ! file_exists ( $fs_dir = dirname ( $filename )))
{
self :: mkdir_recursive ( $fs_dir , 0700 , true );
}
if ( ! ( $dest = fopen ( $filename , 'w' )))
{
throw new egw_exception_assertion_failed ( __METHOD__ . " (): fopen( $filename ,'w') failed! " );
}
if (( $bytes = stream_copy_to_stream ( $content , $dest )) != $fs_size )
{
throw new egw_exception_assertion_failed ( __METHOD__ . " (): fs_id= $fs_id ( $fs_name ) $bytes bytes copied != size of $fs_size bytes! " );
}
if ( $debug ) echo " $fs_id : $fs_name : $bytes bytes copied to fs \n " ;
fclose ( $dest );
fclose ( $content ); unset ( $content );
++ $n ;
}
unset ( $stmt );
if ( $n ) // delete all content in DB, if there was some AND no error (exception thrown!)
{
$query = 'UPDATE ' . self :: TABLE . ' SET fs_content=NULL WHERE fs_content IS NOT NULL' ;
$stmt = self :: $pdo -> prepare ( $query );
$stmt -> execute ();
}
}
return $n ;
}
2008-02-18 07:52:07 +01:00
}
stream_register_wrapper ( sqlfs_stream_wrapper :: SCHEME , 'sqlfs_stream_wrapper' );