How to use url_stat method of has class

Best VfsStream code snippet using has.url_stat

class.sqlfs_stream_wrapper.inc.php

Source:class.sqlfs_stream_wrapper.inc.php Github

copy

Full Screen

...44 * Scheme / protocoll used for this stream-wrapper45 */46 const SCHEME = 'sqlfs';47 /**48 * Does url_stat returns a mime type, or has it to be determined otherwise (string with attribute name)49 */50 const STAT_RETURN_MIME_TYPE = 'mime';51 /**52 * Our tablename53 */54 const TABLE = 'egw_sqlfs';55 /**56 * Name of our property table57 */58 const PROPS_TABLE = 'egw_sqlfs_props';59 /**60 * mode-bits, which have to be set for files61 */62 const MODE_FILE = 0100000;63 /**64 * mode-bits, which have to be set for directories65 */66 const MODE_DIR = 040000;67 /**68 * mode-bits, which have to be set for links69 */70 const MODE_LINK = 0120000;71 /**72 * How much should be logged to the apache error-log73 *74 * 0 = Nothing75 * 1 = only errors76 * 2 = all function calls and errors (contains passwords too!)77 * 3 = log line numbers in sql statements78 */79 const LOG_LEVEL = 1;80 /**81 * We store the content in the DB (no versioning)82 */83 const STORE2DB = 1;84 /**85 * We store the content in the filesystem (egw_info/server/files_dir) (no versioning)86 */87 const STORE2FS = 2;88 /**89 * default for operation, change that if you want to test with STORE2DB atm90 */91 const DEFAULT_OPERATION = self::STORE2FS;92 /**93 * operation mode of the opened file94 *95 * @var int96 */97 protected $operation = self::DEFAULT_OPERATION;98 /**99 * optional context param when opening the stream, null if no context passed100 *101 * @var mixed102 */103 var $context;104 /**105 * Path off the file opened by stream_open106 *107 * @var string108 */109 protected $opened_path;110 /**111 * Mode of the file opened by stream_open112 *113 * @var int114 */115 protected $opened_mode;116 /**117 * Stream of the opened file, either from the DB via PDO or the filesystem118 *119 * @var resource120 */121 protected $opened_stream;122 /**123 * fs_id of opened file124 *125 * @var int126 */127 protected $opened_fs_id;128 /**129 * Cache containing stat-infos from previous url_stat calls AND dir_opendir calls130 *131 * It's values are the columns read from the DB (fs_*), not the ones returned by url_stat!132 *133 * @var array $path => info-array pairs134 */135 static protected $stat_cache = array();136 /**137 * Reference to the PDO object we use138 *139 * @var PDO140 */141 static protected $pdo;142 /**143 * Array with filenames of dir opened with dir_opendir144 *145 * @var array146 */147 protected $opened_dir;148 /**149 * Extra columns added since the intitial introduction of sqlfs150 *151 * Can be set to empty, so get queries running on old versions of sqlfs, eg. for schema updates152 *153 * @var string;154 */155 static public $extra_columns = ',fs_link';156 /**157 * Clears our stat-cache158 *159 * Normaly not necessary, as it is automatically cleared/updated, UNLESS egw_vfs::$user changes!160 *161 * @param string $path='/'162 */163 public static function clearstatcache($path='/')164 {165 //error_log(__METHOD__."('$path')");166 self::$stat_cache = array();167 $GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME,self::$extended_acl = null);168 }169 /**170 * This method is called immediately after your stream object is created.171 *172 * @param string $url URL that was passed to fopen() and that this object is expected to retrieve173 * @param string $mode mode used to open the file, as detailed for fopen()174 * @param int $options additional flags set by the streams API (or'ed together):175 * - STREAM_USE_PATH If path is relative, search for the resource using the include_path.176 * - STREAM_REPORT_ERRORS If this flag is set, you are responsible for raising errors using trigger_error() during opening of the stream.177 * If this flag is not set, you should not raise any errors.178 * @param string &$opened_path full path of the file/resource, if the open was successfull and STREAM_USE_PATH was set179 * @param array $overwrite_new=null if set create new file with values overwriten by the given ones180 * @param string $class=__CLASS__ class to use to call static methods, eg url_stat (workaround for no late static binding in php < 5.3)181 * @todo remove $class parameter and use static::url_stat() once we require PHP5.3!182 * @return boolean true if the ressource was opened successful, otherwise false183 */184 function stream_open ( $url, $mode, $options, &$opened_path, array $overwrite_new=null, $class=__CLASS__ )185 {186 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)");187 $path = parse_url($url,PHP_URL_PATH);188 $this->operation = self::url2operation($url);189 $dir = egw_vfs::dirname($url);190 $this->opened_path = $path;191 $this->opened_mode = $mode = str_replace('b','',$mode); // we are always binary, like every Linux system192 $this->opened_stream = null;193 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 exist194 {195 if ($mode[0] == 'r' || // does $mode require the file to exist (r,r+)196 $mode[0] == 'x' || // or file should not exist, but does197 !($dir_stat=call_user_func(array($class,'url_stat'),$dir,STREAM_URL_STAT_QUIET)) || // or parent dir does not exist create it198 !egw_vfs::check_access($dir,egw_vfs::WRITABLE,$dir_stat)) // or we are not allowed to create it199 {200 self::_remove_password($url);201 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file does not exist or can not be created!");202 if (($options & STREAM_REPORT_ERRORS))203 {204 trigger_error(__METHOD__."($url,$mode,$options) file does not exist or can not be created!",E_USER_WARNING);205 }206 $this->opened_stream = $this->opened_path = $this->opened_mode = null;207 return false;208 }209 // new file --> create it in the DB210 $new_file = true;211 $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'.212 ') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_active)';213 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;214 $stmt = self::$pdo->prepare($query);215 $values = array(216 'fs_name' => egw_vfs::basename($path),217 'fs_dir' => $dir_stat['ino'],218 // we use the mode of the dir, so files in group dirs stay accessible by all members219 'fs_mode' => $dir_stat['mode'] & 0666,220 // for the uid we use the uid of the dir if not 0=root or the current user otherwise221 'fs_uid' => $dir_stat['uid'] ? $dir_stat['uid'] : egw_vfs::$user,222 // we allways use the group of the dir223 'fs_gid' => $dir_stat['gid'],224 'fs_created' => self::_pdo_timestamp(time()),225 'fs_modified' => self::_pdo_timestamp(time()),226 'fs_creator' => egw_vfs::$user,227 'fs_mime' => 'application/octet-stream', // required NOT NULL!228 'fs_size' => 0,229 'fs_active' => self::_pdo_boolean(true),230 );231 if ($overwrite_new) $values = array_merge($values,$overwrite_new);232 if (!$stmt->execute($values) || !($this->opened_fs_id = self::$pdo->lastInsertId('egw_sqlfs_fs_id_seq')))233 {234 $this->opened_stream = $this->opened_path = $this->opened_mode = null;235 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) execute() failed: ".self::$pdo->errorInfo());236 return false;237 }238 if ($this->operation == self::STORE2DB)239 {240 // we buffer all write operations in a temporary file, which get's written on close241 $this->opened_stream = tmpfile();242 }243 // create the hash-dirs, if they not yet exist244 elseif(!file_exists($fs_dir=egw_vfs::dirname(self::_fs_path($this->opened_fs_id))))245 {246 $umaskbefore = umask();247 if (self::LOG_LEVEL > 1) error_log(__METHOD__." about to call mkdir for $fs_dir # Present UMASK:".decoct($umaskbefore)." called from:".function_backtrace());248 self::mkdir_recursive($fs_dir,0700,true);249 }250 }251 // check if opend file is a directory252 elseif($stat && ($stat['mode'] & self::MODE_DIR) == self::MODE_DIR)253 {254 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) Is a directory!");255 if (($options & STREAM_REPORT_ERRORS))256 {257 trigger_error(__METHOD__."($url,$mode,$options) Is a directory!",E_USER_WARNING);258 }259 $this->opened_stream = $this->opened_path = $this->opened_mode = null;260 return false;261 }262 else263 {264 if ($mode == 'r' && !egw_vfs::check_access($url,egw_vfs::READABLE ,$stat) ||// we are not allowed to read265 $mode != 'r' && !egw_vfs::check_access($url,egw_vfs::WRITABLE,$stat)) // or edit it266 {267 self::_remove_password($url);268 $op = $mode == 'r' ? 'read' : 'edited';269 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) file can not be $op!");270 if (($options & STREAM_REPORT_ERRORS))271 {272 trigger_error(__METHOD__."($url,$mode,$options) file can not be $op!",E_USER_WARNING);273 }274 $this->opened_stream = $this->opened_path = $this->opened_mode = null;275 return false;276 }277 $this->opened_fs_id = $stat['ino'];278 if ($this->operation == self::STORE2DB)279 {280 $stmt = self::$pdo->prepare($sql='SELECT fs_content FROM '.self::TABLE.' WHERE fs_id=?');281 $stmt->execute(array($stat['ino']));282 $stmt->bindColumn(1,$this->opened_stream,PDO::PARAM_LOB);283 $stmt->fetch(PDO::FETCH_BOUND);284 // hack to work around a current php bug (http://bugs.php.net/bug.php?id=40913)285 // PDOStatement::bindColumn(,,PDO::PARAM_LOB) is not working for MySQL, content is returned as string :-(286 if (is_string($this->opened_stream))287 {288 $name = md5($url);289 $GLOBALS[$name] =& $this->opened_stream; unset($this->opened_stream);290 require_once(EGW_API_INC.'/class.global_stream_wrapper.inc.php');291 $this->opened_stream = fopen('global://'.$name,'r');292 unset($GLOBALS[$name]); // unset it, so it does not use up memory, once the stream is closed293 }294 //echo 'gettype($this->opened_stream)='; var_dump($this->opened_stream);295 }296 }297 // do we operate directly on the filesystem --> open file from there298 if ($this->operation == self::STORE2FS)299 {300 if (self::LOG_LEVEL > 1) error_log(__METHOD__." fopen (may create a directory? mkdir) ($this->opened_fs_id,$mode,$options)");301 if (!($this->opened_stream = fopen(self::_fs_path($this->opened_fs_id),$mode)) && $new_file)302 {303 // delete db entry again, if we are not able to open a new(!) file304 unset($stmt);305 $stmt = self::$pdo->prepare('DELETE FROM '.self::TABLE.' WHERE fs_id=:fs_id');306 $stmt->execute(array('fs_id' => $this->opened_fs_id));307 }308 }309 if ($mode[0] == 'a') // append modes: a, a+310 {311 $this->stream_seek(0,SEEK_END);312 }313 if (!is_resource($this->opened_stream)) error_log(__METHOD__."($url,$mode,$options) NO stream, returning false!");314 return is_resource($this->opened_stream);315 }316 /**317 * This method is called when the stream is closed, using fclose().318 *319 * You must release any resources that were locked or allocated by the stream.320 */321 function stream_close ( )322 {323 if (self::LOG_LEVEL > 1) error_log(__METHOD__."()");324 if (is_null($this->opened_path) || !is_resource($this->opened_stream) || !$this->opened_fs_id)325 {326 return false;327 }328 if ($this->opened_mode != 'r')329 {330 $this->stream_seek(0,SEEK_END);331 // we need to update the mime-type, size and content (if STORE2DB)332 $values = array(333 'fs_size' => $this->stream_tell(),334 // todo: analyse the file for the mime-type335 'fs_mime' => mime_magic::filename2mime($this->opened_path),336 'fs_id' => $this->opened_fs_id,337 'fs_modifier' => egw_vfs::$user,338 'fs_modified' => self::_pdo_timestamp(time()),339 );340 if ($this->operation == self::STORE2FS)341 {342 $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');343 if (!($ret = $stmt->execute($values)))344 {345 error_log(__METHOD__."() execute() failed! errorInfo()=".array2string(self::$pdo->errorInfo()));346 }347 }348 else349 {350 $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');351 $this->stream_seek(0,SEEK_SET); // rewind to the start352 foreach($values as $name => &$value)353 {354 $stmt->bindParam($name,$value);355 }356 $stmt->bindParam('fs_content', $this->opened_stream, PDO::PARAM_LOB);357 if (!($ret = $stmt->execute()))358 {359 error_log(__METHOD__."() execute() failed! errorInfo()=".array2string(self::$pdo->errorInfo()));360 }361 }362 }363 $ret = fclose($this->opened_stream) && $ret;364 unset(self::$stat_cache[$this->opened_path]);365 $this->opened_stream = $this->opened_path = $this->opened_mode = $this->opend_fs_id = null;366 $this->operation = self::DEFAULT_OPERATION;367 return $ret;368 }369 /**370 * This method is called in response to fread() and fgets() calls on the stream.371 *372 * You must return up-to count bytes of data from the current read/write position as a string.373 * If there are less than count bytes available, return as many as are available.374 * If no more data is available, return either FALSE or an empty string.375 * You must also update the read/write position of the stream by the number of bytes that were successfully read.376 *377 * @param int $count378 * @return string/false up to count bytes read or false on EOF379 */380 function stream_read ( $count )381 {382 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($count) pos=$this->opened_pos");383 if (is_resource($this->opened_stream))384 {385 return fread($this->opened_stream,$count);386 }387 return false;388 }389 /**390 * This method is called in response to fwrite() calls on the stream.391 *392 * You should store data into the underlying storage used by your stream.393 * If there is not enough room, try to store as many bytes as possible.394 * You should return the number of bytes that were successfully stored in the stream, or 0 if none could be stored.395 * You must also update the read/write position of the stream by the number of bytes that were successfully written.396 *397 * @param string $data398 * @return integer399 */400 function stream_write ( $data )401 {402 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($data)");403 if (is_resource($this->opened_stream))404 {405 return fwrite($this->opened_stream,$data);406 }407 return false;408 }409 /**410 * This method is called in response to feof() calls on the stream.411 *412 * Important: PHP 5.0 introduced a bug that wasn't fixed until 5.1: the return value has to be the oposite!413 *414 * if(version_compare(PHP_VERSION,'5.0','>=') && version_compare(PHP_VERSION,'5.1','<'))415 * {416 * $eof = !$eof;417 * }418 *419 * @return boolean true if the read/write position is at the end of the stream and no more data availible, false otherwise420 */421 function stream_eof ( )422 {423 if (is_resource($this->opened_stream))424 {425 return feof($this->opened_stream);426 }427 return false;428 }429 /**430 * This method is called in response to ftell() calls on the stream.431 *432 * @return integer current read/write position of the stream433 */434 function stream_tell ( )435 {436 if (self::LOG_LEVEL > 1) error_log(__METHOD__."()");437 if (is_resource($this->opened_stream))438 {439 return ftell($this->opened_stream);440 }441 return false;442 }443 /**444 * This method is called in response to fseek() calls on the stream.445 *446 * You should update the read/write position of the stream according to offset and whence.447 * See fseek() for more information about these parameters.448 *449 * @param integer $offset450 * @param integer $whence SEEK_SET - Set position equal to offset bytes451 * SEEK_CUR - Set position to current location plus offset.452 * 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.)453 * @return boolean TRUE if the position was updated, FALSE otherwise.454 */455 function stream_seek ( $offset, $whence )456 {457 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($offset,$whence)");458 if (is_resource($this->opened_stream))459 {460 return fseek($this->opened_stream,$offset,$whence);461 }462 return false;463 }464 /**465 * This method is called in response to fflush() calls on the stream.466 *467 * If you have cached data in your stream but not yet stored it into the underlying storage, you should do so now.468 *469 * @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.470 */471 function stream_flush ( )472 {473 if (self::LOG_LEVEL > 1) error_log(__METHOD__."()");474 if (is_resource($this->opened_stream))475 {476 return fflush($this->opened_stream);477 }478 return false;479 }480 /**481 * This method is called in response to fstat() calls on the stream.482 *483 * If you plan to use your wrapper in a require_once you need to define stream_stat().484 * If you plan to allow any other tests like is_file()/is_dir(), you have to define url_stat().485 * stream_stat() must define the size of the file, or it will never be included.486 * url_stat() must define mode, or is_file()/is_dir()/is_executable(), and any of those functions affected by clearstatcache() simply won't work.487 * It's not documented, but directories must be a mode like 040777 (octal), and files a mode like 0100666.488 * If you wish the file to be executable, use 7s instead of 6s.489 * The last 3 digits are exactly the same thing as what you pass to chmod.490 * 040000 defines a directory, and 0100000 defines a file.491 *492 * @return array containing the same values as appropriate for the stream.493 */494 function stream_stat ( )495 {496 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($this->opened_path)");497 return $this->url_stat($this->opened_path,0);498 }499 /**500 * This method is called in response to unlink() calls on URL paths associated with the wrapper.501 *502 * It should attempt to delete the item specified by path.503 * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support unlinking!504 *505 * @param string $url506 * @return boolean TRUE on success or FALSE on failure507 */508 static function unlink ( $url, $parent_stat=null )509 {510 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");511 $path = parse_url($url,PHP_URL_PATH);512 if (!($stat = self::url_stat($path,STREAM_URL_STAT_LINK)) || !egw_vfs::check_access(egw_vfs::dirname($path),egw_vfs::WRITABLE, $parent_stat))513 {514 self::_remove_password($url);515 if (self::LOG_LEVEL) error_log(__METHOD__."($url) permission denied!");516 return false; // no permission or file does not exist517 }518 if ($stat['mime'] == self::DIR_MIME_TYPE)519 {520 self::_remove_password($url);521 if (self::LOG_LEVEL) error_log(__METHOD__."($url) is NO file!");522 return false; // no permission or file does not exist523 }524 $stmt = self::$pdo->prepare('DELETE FROM '.self::TABLE.' WHERE fs_id=:fs_id');525 unset(self::$stat_cache[$path]);526 if (($ret = $stmt->execute(array('fs_id' => $stat['ino']))))527 {528 if (self::url2operation($url) == self::STORE2FS &&529 ($stat['mode'] & self::MODE_LINK) != self::MODE_LINK)530 {531 unlink(self::_fs_path($stat['ino']));532 }533 // delete props534 unset($stmt);535 $stmt = self::$pdo->prepare('DELETE FROM '.self::PROPS_TABLE.' WHERE fs_id=?');536 $stmt->execute(array($stat['ino']));537 }538 return $ret;539 }540 /**541 * This method is called in response to rename() calls on URL paths associated with the wrapper.542 *543 * It should attempt to rename the item specified by path_from to the specification given by path_to.544 * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support renaming.545 *546 * The regular filesystem stream-wrapper returns an error, if $url_from and $url_to are not either both files or both dirs!547 *548 * @param string $url_from549 * @param string $url_to550 * @param string $class=__CLASS__ class to use to call static methods, eg url_stat (workaround for no late static binding in php < 5.3)551 * @todo remove $class parameter and use static::url_stat() and static::unlink() once we require PHP5.3!552 * @return boolean TRUE on success or FALSE on failure553 */554 static function rename ( $url_from, $url_to, $class=__CLASS__)555 {556 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url_from,$url_to)");557 $path_from = parse_url($url_from,PHP_URL_PATH);558 $from_dir = egw_vfs::dirname($path_from);559 $path_to = parse_url($url_to,PHP_URL_PATH);560 $to_dir = egw_vfs::dirname($path_to);561 $operation = self::url2operation($url_from);562 // we have to use array($class,'url_stat'), as $class.'::url_stat' requires PHP 5.2.3 and we currently only require 5.2+563 if (!($from_stat = call_user_func(array($class,'url_stat'),$path_from,0)) ||564 !egw_vfs::check_access($from_dir,egw_vfs::WRITABLE,$from_dir_stat = call_user_func(array($class,'url_stat'),$from_dir,0)))565 {566 self::_remove_password($url_from);567 self::_remove_password($url_to);568 if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_from permission denied!");569 return false; // no permission or file does not exist570 }571 if (!egw_vfs::check_access($to_dir,egw_vfs::WRITABLE,$to_dir_stat = call_user_func(array($class,'url_stat'),$to_dir,0)))572 {573 self::_remove_password($url_from);574 self::_remove_password($url_to);575 if (self::LOG_LEVEL) error_log(__METHOD__."($url_from,$url_to): $path_to permission denied!");576 return false; // no permission or parent-dir does not exist577 }578 // the filesystem stream-wrapper does NOT allow to rename files to directories, as this makes problems579 // for our vfs too, we abort here with an error, like the filesystem one does580 if (($to_stat = call_user_func(array($class,'url_stat'),$path_to,0)) &&581 ($to_stat['mime'] === self::DIR_MIME_TYPE) !== ($from_stat['mime'] === self::DIR_MIME_TYPE))582 {583 self::_remove_password($url_from);584 self::_remove_password($url_to);585 $is_dir = $to_stat['mime'] === self::DIR_MIME_TYPE ? 'a' : 'no';586 if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) $path_to is $is_dir directory!");587 return false; // no permission or file does not exist588 }589 // if destination file already exists, delete it590 if ($to_stat && !call_user_func(array($class,'unlink'),$url_to,$operation))591 {592 self::_remove_password($url_to);593 if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) can't unlink existing $url_to!");594 return false;595 }596 unset(self::$stat_cache[$path_from]);597 unset(self::$stat_cache[$path_to]);598 $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');599 $ok = $stmt->execute(array(600 'fs_dir' => $to_dir_stat['ino'],601 'fs_name' => egw_vfs::basename($path_to),602 'old_dir' => $from_dir_stat['ino'],603 'old_name' => $from_stat['name'],604 ));605 unset($stmt);606 // check if extension changed and update mime-type in that case (as we currently determine mime-type by it's extension!)607 // fixes eg. problems with MsWord storing file with .tmp extension and then renaming to .doc608 if ($ok && ($new_mime = egw_vfs::mime_content_type($url_to,true)) != egw_vfs::mime_content_type($url_to))609 {610 //echo "<p>egw_vfs::nime_content_type($url_to,true) = $new_mime</p>\n";611 $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_mime=:fs_mime WHERE fs_id=:fs_id');612 $stmt->execute(array(613 'fs_mime' => $new_mime,614 'fs_id' => $from_stat['ino'],615 ));616 unset(self::$stat_cache[$path_to]);617 }618 return $ok;619 }620 /**621 * due to problems with recursive directory creation, we have our own here622 */623 private static function mkdir_recursive($pathname, $mode, $depth=0)624 {625 $maxdepth=10;626 $depth2propagate = (int)$depth + 1;627 if ($depth2propagate > $maxdepth) return is_dir($pathname);628 is_dir(egw_vfs::dirname($pathname)) || self::mkdir_recursive(egw_vfs::dirname($pathname), $mode, $depth2propagate);629 return is_dir($pathname) || @mkdir($pathname, $mode);630 }631 /**632 * This method is called in response to mkdir() calls on URL paths associated with the wrapper.633 *634 * It should attempt to create the directory specified by path.635 * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support creating directories.636 *637 * @param string $url638 * @param int $mode639 * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE640 * @return boolean TRUE on success or FALSE on failure641 */642 static function mkdir ( $url, $mode, $options )643 {644 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$mode,$options)");645 if (self::LOG_LEVEL > 1) error_log(__METHOD__." called from:".function_backtrace());646 $path = parse_url($url,PHP_URL_PATH);647 if (self::url_stat($path,STREAM_URL_STAT_QUIET))648 {649 self::_remove_password($url);650 if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) already exist!");651 if (!($options & STREAM_REPORT_ERRORS))652 {653 //throw new Exception(__METHOD__."('$url',$mode,$options) already exist!");654 trigger_error(__METHOD__."('$url',$mode,$options) already exist!",E_USER_WARNING);655 }656 return false;657 }658 $parent_path = egw_vfs::dirname($path);659 if (($query = parse_url($url,PHP_URL_QUERY))) $parent_path .= '?'.$query;660 $parent = self::url_stat($parent_path,STREAM_URL_STAT_QUIET);661 // check if we should also create all non-existing path components and our parent does not exist,662 // if yes call ourself recursive with the parent directory663 if (($options & STREAM_MKDIR_RECURSIVE) && $parent_path != '/' && !$parent)664 {665 if (self::LOG_LEVEL > 1) error_log(__METHOD__." creating parents: $parent_path, $mode");666 if (!self::mkdir($parent_path,$mode,$options))667 {668 return false;669 }670 $parent = self::url_stat($parent_path,0);671 }672 if (!$parent || !egw_vfs::check_access($parent_path,egw_vfs::WRITABLE,$parent))673 {674 self::_remove_password($url);675 if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$mode,$options) permission denied!");676 if (!($options & STREAM_REPORT_ERRORS))677 {678 trigger_error(__METHOD__."('$url',$mode,$options) permission denied!",E_USER_WARNING);679 }680 return false; // no permission or file does not exist681 }682 unset(self::$stat_cache[$path]);683 $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'.684 ') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_size,:fs_mime,:fs_created,:fs_modified,:fs_creator)');685 if (($ok = $stmt->execute(array(686 'fs_name' => egw_vfs::basename($path),687 'fs_dir' => $parent['ino'],688 'fs_mode' => $parent['mode'],689 'fs_uid' => $parent['uid'],690 'fs_gid' => $parent['gid'],691 'fs_size' => 0,692 'fs_mime' => self::DIR_MIME_TYPE,693 'fs_created' => self::_pdo_timestamp(time()),694 'fs_modified' => self::_pdo_timestamp(time()),695 'fs_creator' => egw_vfs::$user,696 ))))697 {698 // check if some other process created the directory parallel to us (sqlfs would gives SQL errors later!)699 $new_fs_id = self::$pdo->lastInsertId('egw_sqlfs_fs_id_seq');700 unset($stmt); // free statement object, on some installs a new prepare fails otherwise!701 $stmt = self::$pdo->prepare($q='SELECT COUNT(*) FROM '.self::TABLE.702 ' WHERE fs_dir=:fs_dir AND fs_active=:fs_active AND fs_name'.self::$case_sensitive_equal.':fs_name');703 if ($stmt->execute(array(704 'fs_dir' => $parent['ino'],705 'fs_active' => self::_pdo_boolean(true),706 'fs_name' => egw_vfs::basename($path),707 )) && $stmt->fetchColumn() > 1) // if there's more then one --> remove our new dir708 {709 self::$pdo->query('DELETE FROM '.self::TABLE.' WHERE fs_id='.$new_fs_id);710 }711 }712 return $ok;713 }714 /**715 * This method is called in response to rmdir() calls on URL paths associated with the wrapper.716 *717 * It should attempt to remove the directory specified by path.718 * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support removing directories.719 *720 * @param string $url721 * @param int $options Possible values include STREAM_REPORT_ERRORS.722 * @return boolean TRUE on success or FALSE on failure.723 */724 static function rmdir ( $url, $options )725 {726 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");727 $path = parse_url($url,PHP_URL_PATH);728 $parent = egw_vfs::dirname($path);729 if (!($stat = self::url_stat($path,0)) || $stat['mime'] != self::DIR_MIME_TYPE ||730 !egw_vfs::check_access($parent,egw_vfs::WRITABLE))731 {732 self::_remove_password($url);733 $err_msg = __METHOD__."($url,$options) ".(!$stat ? 'not found!' :734 ($stat['mime'] != self::DIR_MIME_TYPE ? 'not a directory!' : 'permission denied!'));735 if (self::LOG_LEVEL) error_log($err_msg);736 if (!($options & STREAM_REPORT_ERRORS))737 {738 trigger_error($err_msg,E_USER_WARNING);739 }740 return false; // no permission or file does not exist741 }742 $stmt = self::$pdo->prepare('SELECT COUNT(*) FROM '.self::TABLE.' WHERE fs_dir=?');743 $stmt->execute(array($stat['ino']));744 if ($stmt->fetchColumn())745 {746 self::_remove_password($url);747 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$options) dir is not empty!");748 if (!($options & STREAM_REPORT_ERRORS))749 {750 trigger_error(__METHOD__."('$url',$options) dir is not empty!",E_USER_WARNING);751 }752 return false;753 }754 unset(self::$stat_cache[$path]);755 unset($stmt); // free statement object, on some installs a new prepare fails otherwise!756 $stmt = self::$pdo->prepare('DELETE FROM '.self::TABLE.' WHERE fs_id=?');757 if (($ret = $stmt->execute(array($stat['ino']))))758 {759 self::eacl($path,null,false,$stat['ino']); // remove all (=false) evtl. existing extended acl for that dir760 // delete props761 unset($stmt);762 $stmt = self::$pdo->prepare('DELETE FROM '.self::PROPS_TABLE.' WHERE fs_id=?');763 $stmt->execute(array($stat['ino']));764 }765 return $ret;766 }767 /**768 * This is not (yet) a stream-wrapper function, but it's necessary and can be used static769 *770 * @param string $url771 * @param int $time=null modification time (unix timestamp), default null = current time772 * @param int $atime=null access time (unix timestamp), default null = current time, not implemented in the vfs!773 */774 static function touch($url,$time=null,$atime=null)775 {776 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url)");777 $path = parse_url($url,PHP_URL_PATH);778 if (!($stat = self::url_stat($path,STREAM_URL_STAT_QUIET)))779 {780 // file does not exist --> create an empty one781 if (!($f = fopen(self::SCHEME.'://default'.$path,'w')) || !fclose($f))782 {783 return false;784 }785 if (is_null($time))786 {787 return true; // new (empty) file created with current mod time788 }789 $stat = self::url_stat($path,0);790 }791 unset(self::$stat_cache[$path]);792 $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_modified=:fs_modified,fs_modifier=:fs_modifier WHERE fs_id=:fs_id');793 return $stmt->execute(array(794 'fs_modified' => self::_pdo_timestamp($time ? $time : time()),795 'fs_modifier' => egw_vfs::$user,796 'fs_id' => $stat['ino'],797 ));798 }799 /**800 * Chown command, not yet a stream-wrapper function, but necessary801 *802 * @param string $url803 * @param int $owner804 * @return boolean805 */806 static function chown($url,$owner)807 {808 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");809 $path = parse_url($url,PHP_URL_PATH);810 if (!($stat = self::url_stat($path,0)))811 {812 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!");813 trigger_error("No such file or directory $url !",E_USER_WARNING);814 return false;815 }816 if (!egw_vfs::$is_root)817 {818 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only root can do that!");819 trigger_error("Only root can do that!",E_USER_WARNING);820 return false;821 }822 if ($owner < 0 || $owner && !$GLOBALS['egw']->accounts->id2name($owner)) // not a user (0 == root)823 {824 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) unknown (numeric) user id!");825 trigger_error(__METHOD__."($url,$owner) Unknown (numeric) user id!",E_USER_WARNING);826 //throw new Exception(__METHOD__."($url,$owner) Unknown (numeric) user id!");827 return false;828 }829 $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_uid=:fs_uid WHERE fs_id=:fs_id');830 return $stmt->execute(array(831 'fs_uid' => (int) $owner,832 'fs_id' => $stat['ino'],833 ));834 }835 /**836 * Chgrp command, not yet a stream-wrapper function, but necessary837 *838 * @param string $url839 * @param int $group840 * @return boolean841 */842 static function chgrp($url,$owner)843 {844 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");845 $path = parse_url($url,PHP_URL_PATH);846 if (!($stat = self::url_stat($path,0)))847 {848 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!");849 trigger_error("No such file or directory $url !",E_USER_WARNING);850 return false;851 }852 if (!egw_vfs::has_owner_rights($path,$stat))853 {854 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only owner or root can do that!");855 trigger_error("Only owner or root can do that!",E_USER_WARNING);856 return false;857 }858 if ($owner < 0) $owner = -$owner; // sqlfs uses a positiv group id's!859 if ($owner && !$GLOBALS['egw']->accounts->id2name(-$owner)) // not a group860 {861 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) unknown (numeric) group id!");862 trigger_error("Unknown (numeric) group id!",E_USER_WARNING);863 return false;864 }865 $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_gid=:fs_gid WHERE fs_id=:fs_id');866 return $stmt->execute(array(867 'fs_gid' => $owner,868 'fs_id' => $stat['ino'],869 ));870 }871 /**872 * Chmod command, not yet a stream-wrapper function, but necessary873 *874 * @param string $url875 * @param int $mode876 * @return boolean877 */878 static function chmod($url,$mode)879 {880 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$owner)");881 $path = parse_url($url,PHP_URL_PATH);882 if (!($stat = self::url_stat($path,0)))883 {884 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no such file or directory!");885 trigger_error("No such file or directory $url !",E_USER_WARNING);886 return false;887 }888 if (!egw_vfs::has_owner_rights($path,$stat))889 {890 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) only owner or root can do that!");891 trigger_error("Only owner or root can do that!",E_USER_WARNING);892 return false;893 }894 if (!is_numeric($mode)) // not a mode895 {896 if (self::LOG_LEVEL) error_log(__METHOD__."($url,$owner) no (numeric) mode!");897 trigger_error("No (numeric) mode!",E_USER_WARNING);898 return false;899 }900 $stmt = self::$pdo->prepare('UPDATE '.self::TABLE.' SET fs_mode=:fs_mode WHERE fs_id=:fs_id');901 return $stmt->execute(array(902 'fs_mode' => ((int) $mode) & 0777, // we dont store the file and dir bits, give int overflow!903 'fs_id' => $stat['ino'],904 ));905 }906 /**907 * This method is called immediately when your stream object is created for examining directory contents with opendir().908 *909 * @param string $path URL that was passed to opendir() and that this object is expected to explore.910 * @param $options911 * @return booelan912 */913 function dir_opendir ( $url, $options )914 {915 $this->opened_dir = null;916 $path = parse_url($url,PHP_URL_PATH);917 if (!($stat = self::url_stat($url,0)) || // dir not found918 $stat['mime'] != self::DIR_MIME_TYPE || // no dir919 !egw_vfs::check_access($url,egw_vfs::EXECUTABLE|egw_vfs::READABLE,$stat)) // no access920 {921 self::_remove_password($url);922 $msg = $stat['mime'] != self::DIR_MIME_TYPE ? "$url is no directory" : 'permission denied';923 if (self::LOG_LEVEL) error_log(__METHOD__."('$url',$options) $msg!");924 $this->opened_dir = null;925 return false;926 }927 $this->opened_dir = array();928 $query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.self::$extra_columns.929 ' FROM '.self::TABLE.' WHERE fs_dir=? AND fs_active='.self::_pdo_boolean(true).930 " ORDER BY fs_mime='httpd/unix-directory' DESC, fs_name ASC";931 //if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;932 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__."($url,$options)".' */ '.$query;933 $stmt = self::$pdo->prepare($query);934 $stmt->setFetchMode(PDO::FETCH_ASSOC);935 if ($stmt->execute(array($stat['ino'])))936 {937 foreach($stmt as $file)938 {939 $this->opened_dir[] = $file['fs_name'];940 self::$stat_cache[egw_vfs::concat($path,$file['fs_name'])] = $file;941 }942 }943 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$options): ".implode(', ',$this->opened_dir));944 reset($this->opened_dir);945 return true;946 }947 /**948 * This method is called in response to stat() calls on the URL paths associated with the wrapper.949 *950 * It should return as many elements in common with the system function as possible.951 * Unknown or unavailable values should be set to a rational value (usually 0).952 *953 * If you plan to use your wrapper in a require_once you need to define stream_stat().954 * If you plan to allow any other tests like is_file()/is_dir(), you have to define url_stat().955 * stream_stat() must define the size of the file, or it will never be included.956 * url_stat() must define mode, or is_file()/is_dir()/is_executable(), and any of those functions affected by clearstatcache() simply won't work.957 * It's not documented, but directories must be a mode like 040777 (octal), and files a mode like 0100666.958 * If you wish the file to be executable, use 7s instead of 6s.959 * The last 3 digits are exactly the same thing as what you pass to chmod.960 * 040000 defines a directory, and 0100000 defines a file.961 *962 * @param string $path963 * @param int $flags holds additional flags set by the streams API. It can hold one or more of the following values OR'd together:964 * - STREAM_URL_STAT_LINK For resources with the ability to link to other resource (such as an HTTP Location: forward,965 * or a filesystem symlink). This flag specified that only information about the link itself should be returned,966 * not the resource pointed to by the link.967 * This flag is set in response to calls to lstat(), is_link(), or filetype().968 * - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set,969 * you are responsible for reporting errors using the trigger_error() function during stating of the path.970 * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!971 * @param boolean $eacl_access=null allows extending classes to pass the value of their check_extended_acl() method (no lsb!)972 * @return array973 */974 static function url_stat ( $url, $flags, $eacl_access=null )975 {976 static $max_subquery_depth;977 if (is_null($max_subquery_depth))978 {979 $max_subquery_depth = $GLOBALS['egw_info']['server']['max_subquery_depth'];980 if (!$max_subquery_depth) $max_subquery_depth = 7; // setting current default of 7, if nothing set981 }982 if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags,$eacl_access)");983 $path = parse_url($url,PHP_URL_PATH);984 // webdav adds a trailing slash to dirs, which causes url_stat to NOT find the file otherwise985 if ($path != '/' && substr($path,-1) == '/')986 {987 $path = substr($path,0,-1);988 }989 if (empty($path))990 {991 return false; // is invalid and gives sql error992 }993 // check if we already have the info from the last dir_open call, as the old vfs reads it anyway from the db994 if (self::$stat_cache && isset(self::$stat_cache[$path]) && (is_null($eacl_access) || self::$stat_cache[$path] !== false))995 {996 return self::$stat_cache[$path] ? self::_vfsinfo2stat(self::$stat_cache[$path]) : false;997 }998 if (!is_object(self::$pdo))999 {1000 self::_pdo();1001 }1002 $base_query = 'SELECT fs_id,fs_name,fs_mode,fs_uid,fs_gid,fs_size,fs_mime,fs_created,fs_modified'.self::$extra_columns.1003 ' FROM '.self::TABLE.' WHERE fs_active='.self::_pdo_boolean(true).1004 ' AND fs_name'.self::$case_sensitive_equal.'? AND fs_dir=';1005 $parts = explode('/',$path);1006 // if we have extendes acl access to the url, we dont need and can NOT include the sql for the readable check1007 if (is_null($eacl_access))1008 {1009 $eacl_access = self::check_extended_acl($path,egw_vfs::READABLE); // should be static::check_extended_acl, but no lsb!1010 }1011 try {1012 foreach($parts as $n => $name)1013 {1014 if ($n == 0)1015 {1016 $query = (int) ($path != '/'); // / always has fs_id == 1, no need to query it ($path=='/' needs fs_dir=0!)1017 }1018 elseif ($n < count($parts)-1)1019 {1020 // MySQL 5.0 has a nesting limit for subqueries1021 // --> we replace the so far cumulated subqueries with their result1022 // no idea about the other DBMS, but this does NOT hurt ...1023 // --> depth limit of subqueries is now dynamicly decremented in catch1024 if ($n > 1 && !(($n-1) % $max_subquery_depth) && !($query = self::$pdo->query($query)->fetchColumn()))1025 {1026 if (self::LOG_LEVEL > 1)1027 {1028 self::_remove_password($url);1029 error_log(__METHOD__."('$url',$flags) file or directory not found!");1030 }1031 // we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)1032 return self::$stat_cache[$path] = false;1033 }1034 $query = 'SELECT fs_id FROM '.self::TABLE.' WHERE fs_dir=('.$query.') AND fs_active='.1035 self::_pdo_boolean(true).' AND fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name);1036 // 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)1037 if (!egw_vfs::$is_root && !$eacl_access)1038 {1039 if (!egw_vfs::$user)1040 {1041 self::_remove_password($url);1042 if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$url',$flags) permission denied, no user-id and not root!");1043 return false;1044 }1045 $query .= ' AND '.self::_sql_readable();1046 }1047 }1048 else1049 {1050 $query = str_replace('fs_name'.self::$case_sensitive_equal.'?','fs_name'.self::$case_sensitive_equal.self::$pdo->quote($name),$base_query).'('.$query.')';1051 }1052 }1053 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__."($url,$flags,$eacl_access)".' */ '.$query;1054 //if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;1055 if (!($result = self::$pdo->query($query)) || !($info = $result->fetch(PDO::FETCH_ASSOC)))1056 {1057 if (self::LOG_LEVEL > 1)1058 {1059 self::_remove_password($url);1060 error_log(__METHOD__."('$url',$flags) file or directory not found!");1061 }1062 // we also store negatives (all methods creating new files/dirs have to unset the stat-cache!)1063 return self::$stat_cache[$path] = false;1064 }1065 }1066 catch (PDOException $e) {1067 // decrement subquery limit by 1 and try again, if not already smaller then 31068 if ($max_subquery_depth < 3)1069 {1070 throw new egw_exception_db($e->getMessage());1071 }1072 $GLOBALS['egw_info']['server']['max_subquery_depth'] = --$max_subquery_depth;1073 error_log(__METHOD__."() decremented max_subquery_depth to $max_subquery_depth");1074 config::save_value('max_subquery_depth', $max_subquery_depth, 'phpgwapi');1075 if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) $GLOBALS['egw']->invalidate_session_cache();1076 return self::url_stat($url, $flags, $eacl_access);1077 }1078 self::$stat_cache[$path] = $info;1079 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$flags)=".array2string($info));1080 return self::_vfsinfo2stat($info);1081 }1082 /**1083 * Return readable check as sql (to be AND'ed into the query), only use if !egw_vfs::$is_root1084 *1085 * @return string1086 */1087 protected function _sql_readable()1088 {1089 static $sql_read_acl;1090 if (is_null($sql_read_acl))1091 {1092 foreach($GLOBALS['egw']->accounts->memberships(egw_vfs::$user,true) as $gid)1093 {1094 $memberships[] = abs($gid); // sqlfs stores the gid's positiv1095 }1096 // using octal numbers with mysql leads to funny results (select 384 & 0400 --> 384 not 256=0400)1097 // 256 = 0400, 32 = 0401098 $sql_read_acl = '((fs_mode & 4)=4 OR (fs_mode & 256)=256 AND fs_uid='.(int)egw_vfs::$user.1099 ($memberships ? ' OR (fs_mode & 32)=32 AND fs_gid IN('.implode(',',$memberships).')' : '').')';1100 //error_log(__METHOD__."() egw_vfs::\$user=".array2string(egw_vfs::$user).' --> memberships='.array2string($memberships).' --> '.$sql_read_acl.($memberships?'':': '.function_backtrace()));1101 }1102 return $sql_read_acl;1103 }1104 /**1105 * This method is called in response to readdir().1106 *1107 * It should return a string representing the next filename in the location opened by dir_opendir().1108 *1109 * @return string1110 */1111 function dir_readdir ( )1112 {1113 if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )");1114 if (!is_array($this->opened_dir)) return false;1115 $file = current($this->opened_dir); next($this->opened_dir);1116 return $file;1117 }1118 /**1119 * This method is called in response to rewinddir().1120 *1121 * It should reset the output generated by dir_readdir(). i.e.:1122 * The next call to dir_readdir() should return the first entry in the location returned by dir_opendir().1123 *1124 * @return boolean1125 */1126 function dir_rewinddir ( )1127 {1128 if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )");1129 if (!is_array($this->opened_dir)) return false;1130 reset($this->opened_dir);1131 return true;1132 }1133 /**1134 * This method is called in response to closedir().1135 *1136 * You should release any resources which were locked or allocated during the opening and use of the directory stream.1137 *1138 * @return boolean1139 */1140 function dir_closedir ( )1141 {1142 if (self::LOG_LEVEL > 1) error_log(__METHOD__."( )");1143 if (!is_array($this->opened_dir)) return false;1144 $this->opened_dir = null;1145 return true;1146 }1147 /**1148 * This method is called in response to readlink().1149 *1150 * The readlink value is read by url_stat or dir_opendir and therefore cached in the stat-cache.1151 *1152 * @param string $url1153 * @return string|boolean content of the symlink or false if $url is no symlink (or not found)1154 */1155 static function readlink($path)1156 {1157 $link = !($lstat = self::url_stat($path,STREAM_URL_STAT_LINK)) || is_null($lstat['readlink']) ? false : $lstat['readlink'];1158 if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$path') = $link");1159 return $link;1160 }1161 /**1162 * Method called for symlink()1163 *1164 * @param string $target1165 * @param string $link1166 * @return boolean true on success false on error1167 */1168 static function symlink($target,$link)1169 {1170 if (self::LOG_LEVEL > 1) error_log(__METHOD__."('$target','$link')");1171 if (self::url_stat($link,0) || !($dir = egw_vfs::dirname($link)) ||1172 !egw_vfs::check_access($dir,egw_vfs::WRITABLE,$dir_stat=self::url_stat($dir,0)))1173 {1174 if (self::LOG_LEVEL > 0) error_log(__METHOD__."('$target','$link') returning false! (!stat('$link') || !is_writable('$dir'))");1175 return false; // $link already exists or parent dir does not1176 }1177 $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'.1178 ') VALUES (:fs_name,:fs_dir,:fs_mode,:fs_uid,:fs_gid,:fs_created,:fs_modified,:fs_creator,:fs_mime,:fs_size,:fs_link)';1179 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;1180 $stmt = self::$pdo->prepare($query);1181 unset(self::$stat_cache[parse_url($link,PHP_URL_PATH)]);1182 return !!$stmt->execute(array(1183 'fs_name' => egw_vfs::basename($link),1184 'fs_dir' => $dir_stat['ino'],1185 'fs_mode' => ($dir_stat['mode'] & 0666),1186 'fs_uid' => $dir_stat['uid'] ? $dir_stat['uid'] : egw_vfs::$user,1187 'fs_gid' => $dir_stat['gid'],1188 'fs_created' => self::_pdo_timestamp(time()),1189 'fs_modified' => self::_pdo_timestamp(time()),1190 'fs_creator' => egw_vfs::$user,1191 'fs_mime' => self::SYMLINK_MIME_TYPE,1192 'fs_size' => bytes($target),1193 'fs_link' => $target,1194 ));1195 }1196 private static $extended_acl;1197 /**1198 * Check if extendes ACL (stored in eGW's ACL table) grants access1199 *1200 * The extended ACL is inherited, so it's valid for all subdirs and the included files!1201 * The used algorithm break on the first match. It could be used, to disallow further access.1202 *1203 * @param string $url url to check1204 * @param int $check mode to check: one or more or'ed together of: 4 = read, 2 = write, 1 = executable1205 * @return boolean1206 */1207 static function check_extended_acl($url,$check)1208 {1209 $url_path = parse_url($url,PHP_URL_PATH);1210 if (is_null(self::$extended_acl))1211 {1212 self::_read_extended_acl();1213 }1214 $access = false;1215 foreach(self::$extended_acl as $path => $rights)1216 {1217 if ($path == $url_path || substr($url_path,0,strlen($path)+1) == $path.'/')1218 {1219 $access = ($rights & $check) == $check;1220 break;1221 }1222 }1223 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$check) ".($access?"access granted by $path=$rights":'no access!!!'));1224 return $access;1225 }1226 /**1227 * Read the extended acl via acl::get_grants('sqlfs')1228 *1229 */1230 static protected function _read_extended_acl()1231 {1232 if ((self::$extended_acl = $GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME)) != false)1233 {1234 return; // ext. ACL read from session.1235 }1236 self::$extended_acl = array();1237 if (($rights = $GLOBALS['egw']->acl->get_all_location_rights(egw_vfs::$user,self::EACL_APPNAME)))1238 {1239 $pathes = self::id2path(array_keys($rights));1240 }1241 foreach($rights as $fs_id => $right)1242 {1243 $path = $pathes[$fs_id];1244 if (isset($path))1245 {1246 self::$extended_acl[$path] = (int)$right;1247 }1248 }1249 // sort by length descending, to allow more specific pathes to have precedence1250 uksort(self::$extended_acl,create_function('$a,$b','return strlen($b)-strlen($a);'));1251 $GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME,self::$extended_acl);1252 if (self::LOG_LEVEL > 1) error_log(__METHOD__.'() '.array2string(self::$extended_acl));1253 }1254 /**1255 * Appname used with the acl class to store the extended acl1256 */1257 const EACL_APPNAME = 'sqlfs';1258 /**1259 * Set or delete extended acl for a given path and owner (or delete them if is_null($rights)1260 *1261 * 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!1262 *1263 * @param string $path string with path1264 * @param int $rights=null rights to set, or null to delete the entry1265 * @param int|boolean $owner=null owner for whom to set the rights, null for the current user, or false to delete all rights for $path1266 * @param int $fs_id=null fs_id to use, to not query it again (eg. because it's already deleted)1267 * @return boolean true if acl is set/deleted, false on error1268 */1269 static function eacl($path,$rights=null,$owner=null,$fs_id=null)1270 {1271 if ($path[0] != '/')1272 {1273 $path = parse_url($path,PHP_URL_PATH);1274 }1275 if (is_null($fs_id))1276 {1277 if (!($stat = self::url_stat($path,0)))1278 {1279 if (self::LOG_LEVEL) error_log(__METHOD__."($path,$rights,$owner,$fs_id) no such file or directory!");1280 return false; // $path not found1281 }1282 if (!egw_vfs::has_owner_rights($path,$stat)) // not group dir and user is eGW admin1283 {1284 if (self::LOG_LEVEL) error_log(__METHOD__."($path,$rights,$owner,$fs_id) permission denied!");1285 return false; // permission denied1286 }1287 $fs_id = $stat['ino'];1288 }1289 if (is_null($owner))1290 {1291 $owner = egw_vfs::$user;1292 }1293 if (is_null($rights) || $owner === false)1294 {1295 // delete eacl1296 if (is_null($owner) || $owner == egw_vfs::$user ||1297 $owner < 0 && egw_vfs::$user && in_array($owner,$GLOBALS['egw']->accounts->memberships(egw_vfs::$user,true)))1298 {1299 self::$extended_acl = null; // force new read of eACL, as there could be multiple eACL for that path1300 }1301 $ret = $GLOBALS['egw']->acl->delete_repository(self::EACL_APPNAME,$fs_id,(int)$owner);1302 }1303 else1304 {1305 if (isset(self::$extended_acl) && ($owner == egw_vfs::$user ||1306 $owner < 0 && egw_vfs::$user && in_array($owner,$GLOBALS['egw']->accounts->memberships(egw_vfs::$user,true))))1307 {1308 // set rights for this class, if applicable1309 self::$extended_acl[$path] |= $rights;1310 }1311 $ret = $GLOBALS['egw']->acl->add_repository(self::EACL_APPNAME,$fs_id,$owner,$rights);1312 }1313 if ($ret)1314 {1315 $GLOBALS['egw']->session->appsession('extended_acl',self::EACL_APPNAME,self::$extended_acl);1316 }1317 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path,$rights,$owner,$fs_id)=".(int)$ret);1318 return $ret;1319 }1320 /**1321 * Get all ext. ACL set for a path1322 *1323 * Calls itself recursive, to get the parent directories1324 *1325 * @param string $path1326 * @return array|boolean array with array('path'=>$path,'owner'=>$owner,'rights'=>$rights) or false if $path not found1327 */1328 function get_eacl($path)1329 {1330 if (!($stat = self::url_stat($_path=$path,STREAM_URL_STAT_QUIET)))1331 {1332 error_log(__METHOD__.__LINE__.' '.array2string($path).' not found!');1333 return false; // not found1334 }1335 $eacls = array();1336 foreach($GLOBALS['egw']->acl->get_all_rights($stat['ino'],self::EACL_APPNAME) as $owner => $rights)1337 {1338 $eacls[] = array(1339 'path' => $path,1340 'owner' => $owner,1341 'rights' => $rights,1342 'ino' => $stat['ino'],1343 );1344 }1345 if (($path = egw_vfs::dirname($path)))1346 {1347 $eacls = array_merge((array)self::get_eacl($path),$eacls);1348 }1349 // sort by length descending, to show precedence1350 usort($eacls,create_function('$a,$b','return strlen($b["path"])-strlen($a["path"]);'));1351 //error_log(__METHOD__."('$_path') returning ".array2string($eacls));1352 return $eacls;1353 }1354 /**1355 * Return the path of given fs_id(s)1356 *1357 * Searches the stat_cache first and then the db.1358 * Calls itself recursive to to determine the path of the parent/directory1359 *1360 * @param int|array $fs_ids integer fs_id or array of them1361 * @return string|array path or array or pathes indexed by fs_id1362 */1363 static function id2path($fs_ids)1364 {1365 if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($fs_id).')');1366 $ids = (array)$fs_ids;1367 $pathes = array();1368 // first check our stat-cache for the ids1369 foreach(self::$stat_cache as $path => $stat)1370 {1371 if (($key = array_search($stat['fs_id'],$ids)) !== false)1372 {1373 $pathes[$stat['fs_id']] = $path;1374 unset($ids[$key]);1375 if (!$ids)1376 {1377 if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($fs_ids).')='.array2string($pathes).' *from stat_cache*');1378 return is_array($fs_ids) ? $pathes : array_shift($pathes);1379 }1380 }1381 }1382 // now search via the database1383 if (count($ids) > 1) array_map(create_function('&$v','$v = (int)$v;'),$ids);1384 $query = 'SELECT fs_id,fs_dir,fs_name FROM '.self::TABLE.' WHERE fs_id'.1385 (count($ids) == 1 ? '='.(int)$ids[0] : ' IN ('.implode(',',$ids).')');1386 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;1387 if (!is_object(self::$pdo))1388 {1389 self::_pdo();1390 }1391 $stmt = self::$pdo->prepare($query);1392 $stmt->setFetchMode(PDO::FETCH_ASSOC);1393 if (!$stmt->execute())1394 {1395 return false; // not found1396 }1397 $parents = array();1398 foreach($stmt as $row)1399 {1400 if ($row['fs_dir'] > 1 && !in_array($row['fs_dir'],$parents))1401 {1402 $parents[] = $row['fs_dir'];1403 }1404 $rows[$row['fs_id']] = $row;1405 }1406 unset($stmt);1407 if ($parents && !($parents = self::id2path($parents)))1408 {1409 return false; // parent not found, should never happen ...1410 }1411 if (self::LOG_LEVEL > 1) error_log(__METHOD__." trying foreach with:".print_r($rows,true)."#");1412 foreach((array)$rows as $fs_id => $row)1413 {1414 $parent = $row['fs_dir'] > 1 ? $parents[$row['fs_dir']] : '';1415 $pathes[$fs_id] = $parent . '/' . $row['fs_name'];1416 }1417 if (self::LOG_LEVEL > 1) error_log(__METHOD__.'('.array2string($fs_ids).')='.array2string($pathes));1418 return is_array($fs_ids) ? $pathes : array_shift($pathes);1419 }1420 /**1421 * Convert a sqlfs-file-info into a stat array1422 *1423 * @param array $info1424 * @return array1425 */1426 static protected function _vfsinfo2stat($info)1427 {1428 $stat = array(1429 'ino' => $info['fs_id'],1430 'name' => $info['fs_name'],1431 'mode' => $info['fs_mode'] |1432 ($info['fs_mime'] == self::DIR_MIME_TYPE ? self::MODE_DIR :1433 ($info['fs_mime'] == self::SYMLINK_MIME_TYPE ? self::MODE_LINK : self::MODE_FILE)), // required by the stream wrapper1434 'size' => $info['fs_size'],1435 'uid' => $info['fs_uid'],1436 'gid' => $info['fs_gid'],1437 'mtime' => strtotime($info['fs_modified']),1438 'ctime' => strtotime($info['fs_created']),1439 'nlink' => $info['fs_mime'] == self::DIR_MIME_TYPE ? 2 : 1,1440 // eGW addition to return some extra values1441 'mime' => $info['fs_mime'],1442 'readlink' => $info['fs_link'],1443 );1444 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($info[name]) = ".array2string($stat));1445 return $stat;1446 }1447 public static $pdo_type;1448 /**1449 * Case sensitive comparison operator, for mysql we use ' COLLATE utf8_bin ='1450 *1451 * @var string1452 */1453 public static $case_sensitive_equal = '=';1454 /**1455 * Create pdo object / connection, as long as pdo is not generally used in eGW1456 *1457 * @return PDO1458 */1459 static protected function _pdo()1460 {1461 $egw_db = isset($GLOBALS['egw_setup']) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db;1462 switch($egw_db->Type)1463 {1464 case 'mysqli':1465 case 'mysqlt':1466 case 'mysql':1467 self::$case_sensitive_equal = '= BINARY ';1468 self::$pdo_type = 'mysql';1469 break;1470 default:1471 self::$pdo_type = $egw_db->Type;1472 break;1473 }1474 switch($type)1475 {1476 default:1477 $dsn = self::$pdo_type.':dbname='.$egw_db->Database.($egw_db->Host ? ';host='.$egw_db->Host.($egw_db->Port ? ';port='.$egw_db->Port : '') : '');1478 break;1479 }1480 // check once if pdo extension and DB specific driver is loaded or can be loaded1481 static $pdo_available;1482 if (is_null($pdo_available))1483 {1484 foreach(array('pdo','pdo_'.self::$pdo_type) as $ext)1485 {1486 check_load_extension($ext,true); // true = throw Exception1487 }1488 $pdo_available = true;1489 }1490 try {1491 self::$pdo = new PDO($dsn,$egw_db->User,$egw_db->Password,array(1492 PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,1493 ));1494 } catch(Exception $e) {1495 // Exception reveals password, so we ignore the exception and connect again without pw, to get the right exception without pw1496 self::$pdo = new PDO($dsn,$egw_db->User,'$egw_db->Password');1497 }1498 // set client charset of the connection1499 $charset = translation::charset();1500 switch(self::$pdo_type)1501 {1502 case 'mysql':1503 if (isset($egw_db->Link_ID->charset2mysql[$charset])) $charset = $egw_db->Link_ID->charset2mysql[$charset];1504 // fall throught1505 case 'pgsql':1506 $query = "SET NAMES '$charset'";1507 break;1508 }1509 if ($query)1510 {1511 self::$pdo->exec($query);1512 }1513 return self::$pdo;1514 }1515 /**1516 * Just a little abstration 'til I know how to organise stuff like that with PDO1517 *1518 * @param mixed $time1519 * @return string Y-m-d H:i:s1520 */1521 static protected function _pdo_timestamp($time)1522 {1523 if (is_numeric($time))1524 {1525 $time = date('Y-m-d H:i:s',$time);1526 }1527 return $time;1528 }1529 /**1530 * Just a little abstration 'til I know how to organise stuff like that with PDO1531 *1532 * @param boolean $val1533 * @return string '1' or '0' for mysql, 'true' or 'false' for everyone else1534 */1535 static protected function _pdo_boolean($val)1536 {1537 if (self::$pdo_type == 'mysql')1538 {1539 return $val ? '1' : '0';1540 }1541 return $val ? 'true' : 'false';1542 }1543 /**1544 * Maximum value for a single hash element (should be 10^N): 10, 100 (default), 1000, ...1545 *1546 * DONT change this value, once you have files stored, they will no longer be found!1547 */1548 const HASH_MAX = 100;1549 /**1550 * Return the path of the stored content of a file if $this->operation == self::STORE2FS1551 *1552 * To limit the number of files stored in one directory, we create a hash from the fs_id:1553 * 1 --> /00/11554 * 34 --> /00/341555 * 123 --> /01/1231556 * 4567 --> /45/45671557 * 99999 --> /09/99/999991558 * --> so one directory contains maximum 2 * HASH_MAY entries (HASH_MAX dirs + HASH_MAX files)1559 * @param int $id id of the file1560 * @return string1561 */1562 static function _fs_path($id)1563 {1564 if (!is_numeric($id))1565 {1566 throw new egw_exception_wrong_parameter(__METHOD__."(id=$id) id has to be an integer!");1567 }1568 if (!isset($GLOBALS['egw_info']['server']['files_dir']))1569 {1570 if (is_object($GLOBALS['egw_setup']->db)) // if we run under setup, query the db for the files dir1571 {1572 $GLOBALS['egw_info']['server']['files_dir'] = $GLOBALS['egw_setup']->db->select('egw_config','config_value',array(1573 'config_name' => 'files_dir',1574 'config_app' => 'phpgwapi',1575 ),__LINE__,__FILE__)->fetchColumn();1576 }1577 }1578 if (!$GLOBALS['egw_info']['server']['files_dir'])1579 {1580 throw new egw_exception_assertion_failed("\$GLOBALS['egw_info']['server']['files_dir'] not set!");1581 }1582 $hash = array();1583 for ($n = $id; $n = (int) ($n / self::HASH_MAX); )1584 {1585 $hash[] = sprintf('%02d',$n % self::HASH_MAX);1586 }1587 if (!$hash) $hash[] = '00'; // we need at least one directory, to not conflict with the dir-names1588 array_unshift($hash,$id);1589 $path = '/sqlfs/'.implode('/',array_reverse($hash));1590 //error_log(__METHOD__."($id) = '$path'");1591 return $GLOBALS['egw_info']['server']['files_dir'].$path;1592 }1593 /**1594 * Replace the password of an url with '...' for error messages1595 *1596 * @param string &$url1597 */1598 static protected function _remove_password(&$url)1599 {1600 $parts = parse_url($url);1601 if ($parts['pass'] || $parts['scheme'])1602 {1603 $url = $parts['scheme'].'://'.($parts['user'] ? $parts['user'].($parts['pass']?':...':'').'@' : '').1604 $parts['host'].$parts['path'];1605 }1606 }1607 /**1608 * Get storage mode from url (get parameter 'storage', eg. ?storage=db)1609 *1610 * @param string|array $url complete url or array of url-parts from parse_url1611 * @return int self::STORE2FS or self::STORE2DB1612 */1613 static function url2operation($url)1614 {1615 $operation = self::DEFAULT_OPERATION;1616 if (strpos(is_array($url) ? $url['query'] : $url,'storage=') !== false)1617 {1618 parse_str(is_array($url) ? $url['query'] : parse_url($url,PHP_URL_QUERY),$query);1619 switch ($query['storage'])1620 {1621 case 'db':1622 $operation = self::STORE2DB;1623 break;1624 case 'fs':1625 default:1626 $operation = self::STORE2FS;1627 break;1628 }1629 }1630 //error_log(__METHOD__."('$url') = $operation (1=DB, 2=FS)");1631 return $operation;1632 }1633 /**1634 * Store properties for a single ressource (file or dir)1635 *1636 * @param string|int $path string with path or integer fs_id1637 * @param array $props array of array with values for keys 'name', 'ns', 'val' (null to delete the prop)1638 * @return boolean true if props are updated, false otherwise (eg. ressource not found)1639 */1640 static function proppatch($path,array $props)1641 {1642 if (self::LOG_LEVEL > 1) error_log(__METHOD__."(".array2string($path).','.array2string($props));1643 if (!is_numeric($path))1644 {1645 if (!($stat = self::url_stat($path,0)))1646 {1647 return false;1648 }1649 $id = $stat['ino'];1650 }1651 elseif(!($path = self::id2path($id=$path)))1652 {1653 return false;1654 }1655 if (!egw_vfs::check_access($path,EGW_ACL_EDIT,$stat))1656 {1657 return false; // permission denied1658 }1659 foreach($props as &$prop)1660 {1661 if (!isset($prop['ns'])) $prop['ns'] = egw_vfs::DEFAULT_PROP_NAMESPACE;1662 if (!isset($prop['val']) || self::$pdo_type != 'mysql') // for non mysql, we have to delete the prop anyway, as there's no REPLACE!1663 {1664 if (!isset($del_stmt))1665 {1666 $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');1667 }1668 $del_stmt->execute(array(1669 'fs_id' => $id,1670 'prop_namespace' => $prop['ns'],1671 'prop_name' => $prop['name'],1672 ));1673 }1674 if (isset($prop['val']))1675 {1676 if (!isset($ins_stmt))1677 {1678 $ins_stmt = self::$pdo->prepare((self::$pdo_type == 'mysql' ? 'REPLACE' : 'INSERT').1679 ' INTO '.self::PROPS_TABLE.' (fs_id,prop_namespace,prop_name,prop_value) VALUES (:fs_id,:prop_namespace,:prop_name,:prop_value)');1680 }1681 if (!$ins_stmt->execute(array(1682 'fs_id' => $id,1683 'prop_namespace' => $prop['ns'],1684 'prop_name' => $prop['name'],1685 'prop_value' => $prop['val'],1686 )))1687 {1688 return false;1689 }1690 }1691 }1692 return true;1693 }1694 /**1695 * Read properties for a ressource (file, dir or all files of a dir)1696 *1697 * @param array|string|int $path_ids (array of) string with path or integer fs_id1698 * @param string $ns='http://egroupware.org/' namespace if propfind should be limited to a single one, use null for all1699 * @return array|boolean false on error ($path_ids does not exist), array with props (values for keys 'name', 'ns', 'value'), or1700 * fs_id/path => array of props for $depth==1 or is_array($path_ids)1701 */1702 static function propfind($path_ids,$ns=egw_vfs::DEFAULT_PROP_NAMESPACE)1703 {1704 $ids = is_array($path_ids) ? $path_ids : array($path_ids);1705 foreach($ids as &$id)1706 {1707 if (!is_numeric($id))1708 {1709 if (!($stat = self::url_stat($id,0)))1710 {1711 if (self::LOG_LEVEL) error_log(__METHOD__."(".array2string($path_ids).",$ns) path '$id' not found!");1712 return false;1713 }1714 $id = $stat['ino'];1715 }1716 }1717 if (count($ids) >= 1) array_map(create_function('&$v','$v = (int)$v;'),$ids);1718 $query = 'SELECT * FROM '.self::PROPS_TABLE.' WHERE (fs_id'.1719 (count($ids) == 1 ? '='.(int)implode('',$ids) : ' IN ('.implode(',',$ids).')').')'.1720 (!is_null($ns) ? ' AND prop_namespace=?' : '');1721 if (self::LOG_LEVEL > 2) $query = '/* '.__METHOD__.': '.__LINE__.' */ '.$query;1722 $stmt = self::$pdo->prepare($query);1723 $stmt->setFetchMode(PDO::FETCH_ASSOC);...

Full Screen

Full Screen

StreamWrapper.php

Source:StreamWrapper.php Github

copy

Full Screen

...146 * you are responsible for reporting errors using the trigger_error() function during stating of the path.147 * stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!148 * @return array149 */150 function url_stat ( $url, $flags )151 {152 $eacl_check=self::check_extended_acl($url,Vfs::READABLE);153 // return vCard as /.entry154 if ( $eacl_check && substr($url,-7) == '/.entry' &&155 (list($app) = array_slice(explode('/',$url),-3,1)) && $app === 'addressbook')156 {157 $ret = array(158 'ino' => '#'.md5($url),159 'name' => '.entry',160 'mode' => self::MODE_FILE|Vfs::READABLE, // required by the stream wrapper161 'size' => 1024, // fmail does NOT attach files with size 0!162 'uid' => 0,163 'gid' => 0,164 'mtime' => time(),165 'ctime' => time(),166 'nlink' => 1,167 // eGW addition to return some extra values168 'mime' => $app == 'addressbook' ? 'text/vcard' : 'text/calendar',169 );170 }171 // if entry directory does not exist --> return fake directory172 elseif (!($ret = parent::url_stat($url,$flags)) && $eacl_check)173 {174 list(,/*$apps*/,/*$app*/,$id,$rel_path) = array_pad(explode('/', Vfs::parse_url($url, PHP_URL_PATH), 5),5,null);175 if ($id && !isset($rel_path))176 {177 $ret = array(178 'ino' => '#'.md5($url),179 'name' => $id,180 'mode' => self::MODE_DIR, // required by the stream wrapper181 'size' => 0,182 'uid' => 0,183 'gid' => 0,184 'mtime' => time(),185 'ctime' => time(),186 'nlink' => 2,187 // eGW addition to return some extra values188 'mime' => Vfs::DIR_MIME_TYPE,189 );190 }191 }192 if (self::DEBUG) error_log(__METHOD__."('$url', $flags) calling parent::url_stat(,,".array2string($eacl_check).') returning '.array2string($ret));193 return $ret;194 }195 /**196 * Set or delete extended acl for a given path and owner (or delete them if is_null($rights)197 *198 * Reimplemented, to NOT call the sqlfs functions, as we dont allow to modify the ACL (defined by the apps)199 *200 * @param string $path string with path201 * @param int $rights =null rights to set, or null to delete the entry202 * @param int/boolean $owner =null owner for whom to set the rights, null for the current user, or false to delete all rights for $path203 * @param int $fs_id =null fs_id to use, to not query it again (eg. because it's already deleted)204 * @return boolean true if acl is set/deleted, false on error205 */206 static function eacl($path,$rights=null,$owner=null,$fs_id=null)207 {208 unset($path, $rights, $owner, $fs_id); // not used, but required by function signature209 return false;210 }211 /**212 * Get all ext. ACL set for a path213 *214 * Reimplemented, to NOT call the sqlfs functions, as we dont allow to modify the ACL (defined by the apps)215 *216 * @param string $path217 * @return array/boolean array with array('path'=>$path,'owner'=>$owner,'rights'=>$rights) or false if $path not found218 */219 static function get_eacl($path)220 {221 unset($path); // not used, but required by function signature222 return false;223 }224 /**225 * mkdir for links226 *227 * Reimplemented as we have no static late binding to allow the extended sqlfs to call our eacl and to set no default rights for entry dirs228 *229 * This method is called in response to mkdir() calls on URL paths associated with the wrapper.230 *231 * It should attempt to create the directory specified by path.232 * In order for the appropriate error message to be returned, do not define this method if your wrapper does not support creating directories.233 *234 * @param string $path235 * @param int $mode not used(!), we inherit 005 for /apps/$app and set 000 for /apps/$app/$id236 * @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE, we allways use recursive!237 * @return boolean TRUE on success or FALSE on failure238 */239 function mkdir($path,$mode,$options)240 {241 unset($mode); // not used, but required by function signature242 if($path[0] != '/')243 {244 if (strpos($path,'?') !== false) $query = Vfs::parse_url($path,PHP_URL_QUERY);245 $path = Vfs::parse_url($path,PHP_URL_PATH).($query ? '?'.$query : '');246 }247 list(,$apps,$app,$id) = explode('/',$path);248 $ret = false;249 if ($apps == 'apps' && $app && !$id || self::check_extended_acl($path,Vfs::WRITABLE)) // app directory itself is allways ok250 {251 $current_is_root = Vfs::$is_root; Vfs::$is_root = true;252 $current_user = Vfs::$user; Vfs::$user = 0;253 $sqlfs = new parent();254 $ret = $sqlfs->mkdir($path,0,$options|STREAM_MKDIR_RECURSIVE);255 if ($id) $sqlfs->chmod($path,0); // no other rights256 Vfs::$user = $current_user;257 Vfs::$is_root = $current_is_root;258 }259 //error_log(__METHOD__."($path,$mode,$options) apps=$apps, app=$app, id=$id: returning $ret");260 return $ret;261 }262 /**263 * This method is called in response to rmdir() calls on URL paths associated with the wrapper.264 *265 * Reimplemented to do nothing (specially not complain), if an entry directory does not exist,266 * as we always report them as existing!267 *268 * @param string $url269 * @param int $options Possible values include STREAM_REPORT_ERRORS.270 * @return boolean TRUE on success or FALSE on failure.271 */272 function rmdir ( $url, $options )273 {274 $path = $url != '/' ? Vfs::parse_url($url,PHP_URL_PATH) : $url;275 list(,/*$apps*/,/*$app*/,/*$id*/,$rest) = explode('/',$path);276 // never delete entry-dir, as it makes attic inaccessible277 if (empty($rest))278 {279 return true;280 }281 return parent::rmdir( $path, $options );282 }283 /**284 * This method is called immediately after your stream object is created.285 *286 * Reimplemented from sqlfs to ensure $this->url_stat is called, to fill sqlfs stat cache with our eacl!287 * And to return vcard for url /apps/addressbook/$id/.entry288 *289 * @param string $url URL that was passed to fopen() and that this object is expected to retrieve290 * @param string $mode mode used to open the file, as detailed for fopen()291 * @param int $options additional flags set by the streams API (or'ed together):292 * - STREAM_USE_PATH If path is relative, search for the resource using the include_path.293 * - STREAM_REPORT_ERRORS If this flag is set, you are responsible for raising errors using trigger_error() during opening of the stream.294 * If this flag is not set, you should not raise any errors.295 * @param string $opened_path full path of the file/resource, if the open was successfull and STREAM_USE_PATH was set296 * @return boolean true if the ressource was opened successful, otherwise false297 */298 function stream_open ( $url, $mode, $options, &$opened_path )299 {300 // the following call is necessary to fill sqlfs_stream_wrapper::$stat_cache, WITH the extendes ACL!301 $stat = $this->url_stat($url,0);302 //error_log(__METHOD__."('$url', '$mode', $options) stat=".array2string($stat));303 // return vCard as /.entry304 if ($stat && $mode[0] == 'r' && substr($url,-7) === '/.entry' &&305 (list($app) = array_slice(explode('/',$url),-3,1)) && $app === 'addressbook')306 {307 list($id) = array_slice(explode('/',$url),-2,1);308 $ab_vcard = new addressbook_vcal('addressbook','text/vcard');309 if (!($charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']))310 {311 $charset = 'utf-8';312 }313 if (!($vcard =& $ab_vcard->getVCard($id, $charset)))314 {315 error_log(__METHOD__."('$url', '$mode', $options) addressbook_vcal::getVCard($id) returned false!");316 return false;317 }318 //error_log(__METHOD__."('$url', '$mode', $options) addressbook_vcal::getVCard($id) returned ".$GLOBALS[$name]);319 $this->opened_stream = fopen('php://temp', 'wb');320 fwrite($this->opened_stream, $vcard);321 fseek($this->opened_stream, 0, SEEK_SET);322 return true;323 }324 // create not existing entry directories on the fly325 if ($mode[0] != 'r' && ($dir = Vfs::dirname($url)) &&326 !parent::url_stat($dir, 0) && self::check_extended_acl($dir, Vfs::WRITABLE))327 {328 $this->mkdir($dir,0,STREAM_MKDIR_RECURSIVE);329 }330 return parent::stream_open($url,$mode,$options,$opened_path);331 }332 /**333 * This method is called immediately when your stream object is created for examining directory contents with opendir().334 *335 * Reimplemented to give no error, if entry directory does not exist.336 *337 * @param string $url URL that was passed to opendir() and that this object is expected to explore.338 * @param $options339 * @return booelan340 */341 function dir_opendir ( $url, $options )342 {343 if (!parent::url_stat($url, STREAM_URL_STAT_QUIET) && $this->url_stat($url, STREAM_URL_STAT_QUIET))344 {345 $this->opened_dir = array();346 return true;347 }348 return parent::dir_opendir($url, $options);349 }350 /**351 * Reimplemented to create an entry directory on the fly AND delete our stat cache!352 *353 * @param string $url354 * @param int $time =null modification time (unix timestamp), default null = current time355 * @param int $atime =null access time (unix timestamp), default null = current time, not implemented in the vfs!356 */357 protected function touch($url,$time=null,$atime=null)358 {359 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($url,$time,$atime)");360 if (!($stat = $this->url_stat($url,STREAM_URL_STAT_QUIET)))361 {362 // file does not exist --> create an empty one363 if (!($f = fopen(self::SCHEME.'://default'.Vfs::parse_url($url,PHP_URL_PATH),'w')) || !fclose($f))364 {365 return false;366 }367 }368 return is_null($time) ? true : parent::touch($url,$time,$atime);369 }370 /**371 * This method is called in response to rename() calls on URL paths associated with the wrapper.372 *373 * Reimplemented to create the entry directory, in case it's only faked to be there.374 *375 * @param string $path_from376 * @param string $path_to377 * @return boolean TRUE on success or FALSE on failure378 */379 function rename ( $path_from, $path_to )380 {381 if (self::LOG_LEVEL > 1) error_log(__METHOD__."($path_from,$path_to)");382 // Check to make sure target _really_ exists, not just fake dir from Links/StreamWrapper383 $path = Vfs::parse_url($path_to, PHP_URL_PATH);384 list(,/*$apps*/,/*$app*/,$id) = explode('/', $path);385 if($id && !parent::url_stat(Vfs::dirname($path_to),STREAM_URL_STAT_QUIET))386 {387 $this->mkdir(Vfs::dirname($path), 0, STREAM_MKDIR_RECURSIVE );388 }389 return parent::rename($path_from,$path_to);390 }391 /**392 * Method called for symlink()393 *394 * Reimplemented to really create (not just fake) an entry directory on the fly395 *396 * @param string $target397 * @param string $link398 * @return boolean true on success false on error399 */400 static function symlink($target,$link)401 {402 $parent = new \EGroupware\Api\Vfs\Links\LinksParent();403 if (!$parent->url_stat($dir = Vfs::dirname($link),0) && self::check_extended_acl($dir,Vfs::WRITABLE))404 {405 $parent->mkdir($dir,0,STREAM_MKDIR_RECURSIVE);406 }407 return parent::symlink($target,$link);408 }409 /**410 * Register this stream-wrapper411 */412 public static function register()413 {414 stream_register_wrapper(self::SCHEME, __CLASS__);415 }416}417StreamWrapper::register();...

Full Screen

Full Screen

vfs-context-links.php

Source:vfs-context-links.php Github

copy

Full Screen

...36var_dump(Vfs::mount(/*"$schema://anonymous@default$infolog_anon_dir"*/"vfs://anonymous@default$infolog_anon_dir", $share_dir = "/home/$sysop/anon-infolog", false, false));37Vfs::$is_root = false;38var_dump(Vfs::mount());39var_dump("Vfs::resolve_url('$share_dir/test.txt')=" . Vfs::resolve_url("$share_dir/test.txt"));40var_dump("Vfs::url_stat('$share_dir/test.txt')=" . json_encode(Vfs::stat("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));41var_dump("Vfs::is_readable('$share_dir/test.txt')=" . json_encode(Vfs::is_readable("$share_dir/test.txt")));42var_dump("fopen('vfs://default$share_dir/test.txt', 'r')", $f = fopen("vfs://default$share_dir/test.txt", 'r'), fread($f, 100), fclose($f));43var_dump("Vfs::propfind('$share_dir/test.txt')", Vfs::propfind("$share_dir/test.txt"));44var_dump("Vfs::proppatch('$share_dir/test.txt', [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])=" . array2string(Vfs::proppatch("$share_dir/test.txt", [['ns' => Vfs::DEFAULT_PROP_NAMESPACE, 'name' => 'test', 'val' => 'something else']])),45 "Vfs::propfind('$share_dir/test.txt')=" . json_encode(Vfs::propfind("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));46var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir")));47var_dump("Vfs::mkdir('$share_dir/test-dir')=" . json_encode(Vfs::mkdir("$share_dir/test-dir")));48var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir"), JSON_UNESCAPED_SLASHES));49var_dump(file_put_contents("vfs://default$share_dir/test-dir/test.txt", "Just a test ;)\n"));50var_dump("Vfs::url_stat('$share_dir/test-dir/test.txt')=" . json_encode(Vfs::stat("$share_dir/test-dir"), JSON_UNESCAPED_SLASHES));51var_dump(file_get_contents("vfs://default$share_dir/test-dir/test.txt"));52var_dump("Vfs::unlink('$share_dir/test-dir/test.txt')=" . json_encode(Vfs::unlink("$share_dir/test-dir/test.txt")));53var_dump("Vfs::rmdir('$share_dir/test-dir')=" . json_encode(Vfs::rmdir("$share_dir/test-dir")));54var_dump("Vfs::url_stat('$share_dir/test-dir')=" . json_encode(Vfs::stat("$share_dir/test-dir")));55var_dump("Vfs::scandir('$share_dir')=" . json_encode(Vfs::scandir($share_dir), JSON_UNESCAPED_SLASHES));56var_dump("Vfs::remove('$share_dir/test.txt')=" . json_encode(Vfs::remove("$share_dir/test.txt"), JSON_UNESCAPED_SLASHES));57var_dump("Vfs::scandir('$share_dir')=" . json_encode(Vfs::scandir($share_dir), JSON_UNESCAPED_SLASHES));...

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$obj = new Has();2$obj->url_stat();3$obj = new Has();4$obj->file_get_contents();5$obj = new Has();6$obj->file();7$obj = new Has();8$obj->file();9$obj = new Has();10$obj->file();11$obj = new Has();12$obj->file();13$obj = new Has();14$obj->file();15$obj = new Has();16$obj->file();17$obj = new Has();18$obj->file();19$obj = new Has();20$obj->file();21$obj = new Has();22$obj->file();23$obj = new Has();24$obj->file();25$obj = new Has();26$obj->file();27$obj = new Has();28$obj->file();29$obj = new Has();30$obj->file();31$obj = new Has();32$obj->file();33$obj = new Has();34$obj->file();35$obj = new Has();36$obj->file();37$obj = new Has();38$obj->file();

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$has = new has();2$has->url_stat();3$has = new has();4$has->file_get_contents();5$has = new has();6$has->get_headers();7$has = new has();8$has->curl();9$has = new has();10$has->fsockopen();11$has = new has();12$has->get_meta_tags();13$has = new has();14$has->getimagesize();15$has = new has();16$has->file_exists();17$has = new has();18$has->file();19$has = new has();20$has->file();21$has = new has();22$has->file();23$has = new has();24$has->file();25$has = new has();26$has->file();27$has = new has();28$has->file();29$has = new has();30$has->file();31$has = new has();32$has->file();33$has = new has();34$has->file();

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$has = new Has();2$has->url_stat();3$has = new Has();4$has->file_exists();5$has = new Has();6$has->is_file();7$has = new Has();8$has->is_dir();9$has = new Has();10$has->is_link();11$has = new Has();12$has->is_readable();13$has = new Has();14$has->is_writable();15$has = new Has();16$has->is_executable();17$has = new Has();18$has->filectime();19$has = new Has();20$has->fileatime();21$has = new Has();22$has->filemtime();23$has = new Has();24$has->filegroup();25$has = new Has();26$has->fileowner();27$has = new Has();28$has->filesize();29$has = new Has();30$has->filetype();31$has = new Has();32$has->disk_free_space();33$has = new Has();

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$has = new Has();2$has->url_stat();3$has = new Has();4$has->get_headers();5$has = new Has();6$has->get_http_response_code();7$has = new Has();8$has->get_headers();9$has = new Has();10$has->file_get_contents();11$has = new Has();12$has->curl_init();13$has = new Has();14$has->file();15$has = new Has();16$has->fopen();17$has = new Has();18$has->fsockopen();19$has = new Has();20$has->socket_create();21$has = new Has();22$has->gethostbyname();23$has = new Has();24$has->gethostbynamel();25$has = new Has();26$has->checkdnsrr();27$has = new Has();28$has->dns_get_record();29$has = new Has();30$has->getmxrr();31$has = new Has();32$has->getprotobyname();

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$has = new Has();2$has->url_stat($url);3$has = new Has();4$has->get_headers($url);5$has = new Has();6$has->curl($url);7$has = new Has();8$has->file_get_contents($url);9$has = new Has();10$has->file($url);11$has = new Has();12$has->getimagesize($url);13$has = new Has();14$has->getimagesizefromstring($url);15$has = new Has();16$has->getimagesizefromstring($url);17$has = new Has();18$has->getimagesizefromstring($url);19$has = new Has();20$has->getimagesizefromstring($url);21$has = new Has();22$has->getimagesizefromstring($url);23$has = new Has();24$has->getimagesizefromstring($url);25$has = new Has();26$has->getimagesizefromstring($url);27$has = new Has();28$has->getimagesizefromstring($url);

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$has = new has();2$has = new has();3$has = new has();4$has = new has();5$has = new has();6$has = new has();7$has = new has();8$has = new has();9$has = new has();10$has = new has();11$has = new has();12$has = new has();13$has = new has();14$has = new has();

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$stat = stat($url);2print_r($stat);3$stat = stat($url);4print_r($stat);5$stat = stat($url);6print_r($stat);7Array ( [dev] => 0 [ino] => 0 [mode] => 33206 [nlink] => 0 [uid] => 0 [gid] => 0 [rdev] => 0 [size] => 0 [atime] => 0 [mtime] => 0 [ctime] => 0 [blksize] => -1 [blocks] => -1 ) Array ( [dev] => 0 [ino] => 0 [mode] => 33206 [nlink] => 0 [uid] => 0 [gid] => 0 [rdev] => 0 [size] => 0 [atime] => 0 [mtime] => 0 [ctime] => 0 [blksize] => -1 [blocks] => -1 ) Array ( [dev] => 0 [ino] => 0 [mode] => 33206 [nlink] => 0 [uid] => 0 [gid] => 0 [rdev] => 0 [size] => 0 [atime] => 0 [mtime] => 0 [ctime] => 0 [blksize] => -1 [blocks] => -1 ) Array ( [dev] => 0 [ino] => 0 [mode] => 33206 [nlink] => 0 [uid] => 0 [gid] => 0 [rdev] => 0 [size] => 0 [atime] => 0 [mtime] => 0 [ctime] => 0 [blksize] => -1 [blocks] => -1 )

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1$stat = stat('test.txt');2if ($stat) {3echo 'File size: ' . $stat[7] . ' bytes';4echo 'Last modified: ' . date('Y-m-d H:i:s', $stat[9]);5} else {6echo 'File not found';7}8if (file_exists('test.txt')) {9echo 'File exists';10} else {11echo 'File not found';12}13if (file_exists('test.txt')) {14echo 'File exists';15} else {16echo 'File not found';17}18if (file_exists('test.txt')) {19echo 'File exists';20} else {21echo 'File not found';22}23if (file_exists('test.txt')) {24echo 'File exists';25} else {26echo 'File not found';27}28if (file_exists('test.txt')) {29echo 'File exists';30} else {31echo 'File not found';32}33if (file_exists('test.txt')) {34echo 'File exists';35} else {36echo 'File not found';37}38if (file_exists('test.txt')) {39echo 'File exists';40} else {41echo 'File not found';42}43if (file_exists('

Full Screen

Full Screen

url_stat

Using AI Code Generation

copy

Full Screen

1include 'has.php';2$ob=new has();3$ob->url_stat();4echo "The file is created at ".date("F d Y H:i:s.", filectime("2.php"));5echo "The file is last accessed at ".date("F d Y H:i:s.", fileatime("2.php"));6echo "The file is last modified at ".date("F d Y H:i:s.", filemtime("2.php"));7PHP | filectime() Function8PHP | fileatime() Function9PHP | filemtime() Function10PHP | Check if file exists using file_exists() function11PHP | Check if file exists using is_file() function12PHP | Check if file exists using file_exists() function13PHP | Check if file exists using is_file() function14PHP | filectime() Function15PHP | fileatime() Function16PHP | filemtime() Function17PHP | Check if file exists using file_exists() function18PHP | Check if file exists using is_file() function19PHP | Check if file exists using file_exists() function20PHP | Check if file exists using is_file() function21PHP | filectime() Function22PHP | fileatime() Function23PHP | filemtime() Function24PHP | Check if file exists using file_exists() function25PHP | Check if file exists using is_file() function26PHP | Check if file exists using file_exists() function27PHP | Check if file exists using is_file() function28PHP | filectime() Function29PHP | fileatime() Function30PHP | filemtime() Function31PHP | Check if file exists using file_exists() function32PHP | Check if file exists using is_file() function33PHP | Check if file exists using file_exists() function34PHP | Check if file exists using is_file() function

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run VfsStream automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Trigger url_stat code on LambdaTest Cloud Grid

Execute automation tests with url_stat on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful