mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-06-26 21:01:52 +02:00
* fixed handling of + char in VFS filenames (using egw_vfs::decodePath() instead of urldecode())
This commit is contained in:
parent
80e44448cc
commit
d6c6dc2de0
804
egw-pear/HTTP/WebDAV/Server/Filesystem.php
Normal file
804
egw-pear/HTTP/WebDAV/Server/Filesystem.php
Normal file
@ -0,0 +1,804 @@
|
||||
<?php
|
||||
|
||||
require_once "HTTP/WebDAV/Server.php";
|
||||
require_once "System.php";
|
||||
|
||||
/**
|
||||
* Filesystem access using WebDAV
|
||||
*
|
||||
* @access public
|
||||
* @author Hartmut Holzgraefe <hartmut@php.net>
|
||||
* @version @package-version@
|
||||
*/
|
||||
class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
|
||||
{
|
||||
/**
|
||||
* Root directory for WebDAV access
|
||||
*
|
||||
* Defaults to webserver document root (set by ServeRequest)
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $base = "";
|
||||
|
||||
/**
|
||||
* MySQL Host where property and locking information is stored
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $db_host = "localhost";
|
||||
|
||||
/**
|
||||
* MySQL database for property/locking information storage
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $db_name = "webdav";
|
||||
|
||||
/**
|
||||
* MySQL table name prefix
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $db_prefix = "";
|
||||
|
||||
/**
|
||||
* MySQL user for property/locking db access
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $db_user = "root";
|
||||
|
||||
/**
|
||||
* MySQL password for property/locking db access
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
var $db_passwd = "";
|
||||
|
||||
/**
|
||||
* Serve a webdav request
|
||||
*
|
||||
* @access public
|
||||
* @param string
|
||||
*/
|
||||
function ServeRequest($base = false)
|
||||
{
|
||||
// special treatment for litmus compliance test
|
||||
// reply on its identifier header
|
||||
// not needed for the test itself but eases debugging
|
||||
foreach (apache_request_headers() as $key => $value) {
|
||||
if (stristr($key, "litmus")) {
|
||||
error_log("Litmus test $value");
|
||||
header("X-Litmus-reply: ".$value);
|
||||
}
|
||||
}
|
||||
|
||||
// set root directory, defaults to webserver document root if not set
|
||||
if ($base) {
|
||||
$this->base = realpath($base); // TODO throw if not a directory
|
||||
} else if (!$this->base) {
|
||||
$this->base = $this->_SERVER['DOCUMENT_ROOT'];
|
||||
}
|
||||
|
||||
// establish connection to property/locking db
|
||||
mysql_connect($this->db_host, $this->db_user, $this->db_passwd) or die(mysql_error());
|
||||
mysql_select_db($this->db_name) or die(mysql_error());
|
||||
// TODO throw on connection problems
|
||||
|
||||
// let the base class do all the work
|
||||
parent::ServeRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* No authentication is needed here
|
||||
*
|
||||
* @access private
|
||||
* @param string HTTP Authentication type (Basic, Digest, ...)
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @return bool true on successful authentication
|
||||
*/
|
||||
function check_auth($type, $user, $pass)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PROPFIND method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @param array return array for file properties
|
||||
* @return bool true on success
|
||||
*/
|
||||
function PROPFIND(&$options, &$files)
|
||||
{
|
||||
// get absolute fs path to requested resource
|
||||
$fspath = $this->base . $options["path"];
|
||||
|
||||
// sanity check
|
||||
if (!file_exists($fspath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// prepare property array
|
||||
$files["files"] = array();
|
||||
|
||||
// store information for the requested path itself
|
||||
$files["files"][] = $this->fileinfo($options["path"]);
|
||||
|
||||
// information for contained resources requested?
|
||||
if (!empty($options["depth"])) { // TODO check for is_dir() first?
|
||||
|
||||
// make sure path ends with '/'
|
||||
$options["path"] = $this->_slashify($options["path"]);
|
||||
|
||||
// try to open directory
|
||||
$handle = @opendir($fspath);
|
||||
|
||||
if ($handle) {
|
||||
// ok, now get all its contents
|
||||
while ($filename = readdir($handle)) {
|
||||
if ($filename != "." && $filename != "..") {
|
||||
$files["files"][] = $this->fileinfo($options["path"].$filename);
|
||||
}
|
||||
}
|
||||
// TODO recursion needed if "Depth: infinite"
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
|
||||
// ok, all done
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get properties for a single file/resource
|
||||
*
|
||||
* @param string resource path
|
||||
* @return array resource properties
|
||||
*/
|
||||
function fileinfo($path)
|
||||
{
|
||||
// map URI path to filesystem path
|
||||
$fspath = $this->base . $path;
|
||||
|
||||
// create result array
|
||||
$info = array();
|
||||
// TODO remove slash append code when base clase is able to do it itself
|
||||
$info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path;
|
||||
$info["props"] = array();
|
||||
|
||||
// no special beautified displayname here ...
|
||||
$info["props"][] = $this->mkprop("displayname", strtoupper($path));
|
||||
|
||||
// creation and modification time
|
||||
$info["props"][] = $this->mkprop("creationdate", filectime($fspath));
|
||||
$info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
|
||||
|
||||
// type and size (caller already made sure that path exists)
|
||||
if (is_dir($fspath)) {
|
||||
// directory (WebDAV collection)
|
||||
$info["props"][] = $this->mkprop("resourcetype", array($this->mkprop('collection', '')));
|
||||
$info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
|
||||
} else {
|
||||
// plain file (WebDAV resource)
|
||||
$info["props"][] = $this->mkprop("resourcetype", "");
|
||||
if (is_readable($fspath)) {
|
||||
$info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath));
|
||||
} else {
|
||||
$info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
|
||||
}
|
||||
$info["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
|
||||
}
|
||||
|
||||
// get additional properties from database
|
||||
$query = "SELECT ns, name, value
|
||||
FROM {$this->db_prefix}properties
|
||||
WHERE path = '$path'";
|
||||
$res = mysql_query($query);
|
||||
while ($row = mysql_fetch_assoc($res)) {
|
||||
$info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
|
||||
}
|
||||
mysql_free_result($res);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* detect if a given program is found in the search PATH
|
||||
*
|
||||
* helper function used by _mimetype() to detect if the
|
||||
* external 'file' utility is available
|
||||
*
|
||||
* @param string program name
|
||||
* @param string optional search path, defaults to $PATH
|
||||
* @return bool true if executable program found in path
|
||||
*/
|
||||
function _can_execute($name, $path = false)
|
||||
{
|
||||
// path defaults to PATH from environment if not set
|
||||
if ($path === false) {
|
||||
$path = getenv("PATH");
|
||||
}
|
||||
|
||||
// check method depends on operating system
|
||||
if (!strncmp(PHP_OS, "WIN", 3)) {
|
||||
// on Windows an appropriate COM or EXE file needs to exist
|
||||
$exts = array(".exe", ".com");
|
||||
$check_fn = "file_exists";
|
||||
} else {
|
||||
// anywhere else we look for an executable file of that name
|
||||
$exts = array("");
|
||||
$check_fn = "is_executable";
|
||||
}
|
||||
|
||||
// now check the directories in the path for the program
|
||||
foreach (explode(PATH_SEPARATOR, $path) as $dir) {
|
||||
// skip invalid path entries
|
||||
if (!file_exists($dir)) continue;
|
||||
if (!is_dir($dir)) continue;
|
||||
|
||||
// and now look for the file
|
||||
foreach ($exts as $ext) {
|
||||
if ($check_fn("$dir/$name".$ext)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* try to detect the mime type of a file
|
||||
*
|
||||
* @param string file path
|
||||
* @return string guessed mime type
|
||||
*/
|
||||
function _mimetype($fspath)
|
||||
{
|
||||
if (@is_dir($fspath)) {
|
||||
// directories are easy
|
||||
return "httpd/unix-directory";
|
||||
} else if (function_exists("mime_content_type")) {
|
||||
// use mime magic extension if available
|
||||
$mime_type = mime_content_type($fspath);
|
||||
} else if ($this->_can_execute("file")) {
|
||||
// it looks like we have a 'file' command,
|
||||
// lets see it it does have mime support
|
||||
$fp = popen("file -i '$fspath' 2>/dev/null", "r");
|
||||
$reply = fgets($fp);
|
||||
pclose($fp);
|
||||
|
||||
// popen will not return an error if the binary was not found
|
||||
// and find may not have mime support using "-i"
|
||||
// so we test the format of the returned string
|
||||
|
||||
// the reply begins with the requested filename
|
||||
if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) {
|
||||
$reply = substr($reply, strlen($fspath)+2);
|
||||
// followed by the mime type (maybe including options)
|
||||
if (preg_match('|^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*|', $reply, $matches)) {
|
||||
$mime_type = $matches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($mime_type)) {
|
||||
// Fallback solution: try to guess the type by the file extension
|
||||
// TODO: add more ...
|
||||
// TODO: it has been suggested to delegate mimetype detection
|
||||
// to apache but this has at least three issues:
|
||||
// - works only with apache
|
||||
// - needs file to be within the document tree
|
||||
// - requires apache mod_magic
|
||||
// TODO: can we use the registry for this on Windows?
|
||||
// OTOH if the server is Windos the clients are likely to
|
||||
// be Windows, too, and tend do ignore the Content-Type
|
||||
// anyway (overriding it with information taken from
|
||||
// the registry)
|
||||
// TODO: have a seperate PEAR class for mimetype detection?
|
||||
switch (strtolower(strrchr(basename($fspath), "."))) {
|
||||
case ".html":
|
||||
$mime_type = "text/html";
|
||||
break;
|
||||
case ".gif":
|
||||
$mime_type = "image/gif";
|
||||
break;
|
||||
case ".jpg":
|
||||
$mime_type = "image/jpeg";
|
||||
break;
|
||||
default:
|
||||
$mime_type = "application/octet-stream";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $mime_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET method handler
|
||||
*
|
||||
* @param array parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function GET(&$options)
|
||||
{
|
||||
// get absolute fs path to requested resource
|
||||
$fspath = $this->base . $options["path"];
|
||||
|
||||
// sanity check
|
||||
if (!file_exists($fspath)) return false;
|
||||
|
||||
// is this a collection?
|
||||
if (is_dir($fspath)) {
|
||||
return $this->GetDir($fspath, $options);
|
||||
}
|
||||
|
||||
// detect resource type
|
||||
$options['mimetype'] = $this->_mimetype($fspath);
|
||||
|
||||
// detect modification time
|
||||
// see rfc2518, section 13.7
|
||||
// some clients seem to treat this as a reverse rule
|
||||
// requiering a Last-Modified header if the getlastmodified header was set
|
||||
$options['mtime'] = filemtime($fspath);
|
||||
|
||||
// detect resource size
|
||||
$options['size'] = filesize($fspath);
|
||||
|
||||
// no need to check result here, it is handled by the base class
|
||||
if (!($options['stream'] = fopen($fspath, "r")))
|
||||
{
|
||||
return '403 Forbidden';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET method handler for directories
|
||||
*
|
||||
* This is a very simple mod_index lookalike.
|
||||
* See RFC 2518, Section 8.4 on GET/HEAD for collections
|
||||
*
|
||||
* @param string directory path
|
||||
* @return void function has to handle HTTP response itself
|
||||
*/
|
||||
function GetDir($fspath, &$options)
|
||||
{
|
||||
$path = $this->_slashify($options["path"]);
|
||||
if ($path != $options["path"]) {
|
||||
header("Location: ".$this->base_uri.$path);
|
||||
exit;
|
||||
}
|
||||
|
||||
// fixed width directory column format
|
||||
$format = "%15s %-19s %-s\n";
|
||||
|
||||
$handle = @opendir($fspath);
|
||||
if (!$handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
echo "<html><head><title>Index of ".htmlspecialchars($options['path'])."</title></head>\n";
|
||||
|
||||
echo "<h1>Index of ".htmlspecialchars($options['path'])."</h1>\n";
|
||||
|
||||
echo "<pre>";
|
||||
printf($format, "Size", "Last modified", "Filename");
|
||||
echo "<hr>";
|
||||
|
||||
while ($filename = readdir($handle)) {
|
||||
if ($filename != "." && $filename != "..") {
|
||||
$fullpath = $fspath.$filename;
|
||||
$name = htmlspecialchars($filename);
|
||||
printf($format,
|
||||
number_format(filesize($fullpath)),
|
||||
strftime("%Y-%m-%d %H:%M:%S", filemtime($fullpath)),
|
||||
'<a href="'.$name.'">'.$name.'</a>');
|
||||
}
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
|
||||
closedir($handle);
|
||||
|
||||
echo "</html>\n";
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT method handler
|
||||
*
|
||||
* @param array parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function PUT(&$options)
|
||||
{
|
||||
$fspath = $this->base . $options["path"];
|
||||
|
||||
if (!@is_dir(dirname($fspath))) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
|
||||
$options["new"] = ! file_exists($fspath);
|
||||
|
||||
$fp = fopen($fspath, "w");
|
||||
|
||||
return $fp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MKCOL method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function MKCOL($options)
|
||||
{
|
||||
$path = $this->base .$options["path"];
|
||||
$parent = dirname($path);
|
||||
$name = basename($path);
|
||||
|
||||
if (!file_exists($parent)) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
|
||||
if (!is_dir($parent)) {
|
||||
return "403 Forbidden";
|
||||
}
|
||||
|
||||
if ( file_exists($parent."/".$name) ) {
|
||||
return "405 Method not allowed";
|
||||
}
|
||||
|
||||
if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
|
||||
return "415 Unsupported media type";
|
||||
}
|
||||
|
||||
$stat = mkdir($parent."/".$name, 0777);
|
||||
if (!$stat) {
|
||||
return "403 Forbidden";
|
||||
}
|
||||
|
||||
return ("201 Created");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DELETE method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function DELETE($options)
|
||||
{
|
||||
$path = $this->base . "/" .$options["path"];
|
||||
|
||||
if (!file_exists($path)) {
|
||||
return "404 Not found";
|
||||
}
|
||||
|
||||
if (is_dir($path)) {
|
||||
$query = "DELETE FROM {$this->db_prefix}properties
|
||||
WHERE path LIKE '".$this->_slashify($options["path"])."%'";
|
||||
mysql_query($query);
|
||||
System::rm("-rf $path");
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
$query = "DELETE FROM {$this->db_prefix}properties
|
||||
WHERE path = '$options[path]'";
|
||||
mysql_query($query);
|
||||
|
||||
return "204 No Content";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MOVE method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function MOVE($options)
|
||||
{
|
||||
return $this->COPY($options, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* COPY method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function COPY($options, $del=false)
|
||||
{
|
||||
// TODO Property updates still broken (Litmus should detect this?)
|
||||
|
||||
if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
|
||||
return "415 Unsupported media type";
|
||||
}
|
||||
|
||||
// no copying to different WebDAV Servers yet
|
||||
if (isset($options["dest_url"])) {
|
||||
return "502 bad gateway";
|
||||
}
|
||||
|
||||
$source = $this->base .$options["path"];
|
||||
if (!file_exists($source)) return "404 Not found";
|
||||
|
||||
$dest = $this->base . $options["dest"];
|
||||
$new = !file_exists($dest);
|
||||
$existing_col = false;
|
||||
|
||||
if (!$new) {
|
||||
if ($del && is_dir($dest)) {
|
||||
if (!$options["overwrite"]) {
|
||||
return "412 precondition failed";
|
||||
}
|
||||
$dest .= basename($source);
|
||||
if (file_exists($dest)) {
|
||||
$options["dest"] .= basename($source);
|
||||
} else {
|
||||
$new = true;
|
||||
$existing_col = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$new) {
|
||||
if ($options["overwrite"]) {
|
||||
$stat = $this->DELETE(array("path" => $options["dest"]));
|
||||
if (($stat{0} != "2") && (substr($stat, 0, 3) != "404")) {
|
||||
return $stat;
|
||||
}
|
||||
} else {
|
||||
return "412 precondition failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir($source) && ($options["depth"] != "infinity")) {
|
||||
// RFC 2518 Section 9.2, last paragraph
|
||||
return "400 Bad request";
|
||||
}
|
||||
|
||||
if ($del) {
|
||||
if (!rename($source, $dest)) {
|
||||
return "500 Internal server error";
|
||||
}
|
||||
$destpath = $this->_unslashify($options["dest"]);
|
||||
if (is_dir($source)) {
|
||||
$query = "UPDATE {$this->db_prefix}properties
|
||||
SET path = REPLACE(path, '".$options["path"]."', '".$destpath."')
|
||||
WHERE path LIKE '".$this->_slashify($options["path"])."%'";
|
||||
mysql_query($query);
|
||||
}
|
||||
|
||||
$query = "UPDATE {$this->db_prefix}properties
|
||||
SET path = '".$destpath."'
|
||||
WHERE path = '".$options["path"]."'";
|
||||
mysql_query($query);
|
||||
} else {
|
||||
if (is_dir($source)) {
|
||||
$files = System::find($source);
|
||||
$files = array_reverse($files);
|
||||
} else {
|
||||
$files = array($source);
|
||||
}
|
||||
|
||||
if (!is_array($files) || empty($files)) {
|
||||
return "500 Internal server error";
|
||||
}
|
||||
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (is_dir($file)) {
|
||||
$file = $this->_slashify($file);
|
||||
}
|
||||
|
||||
$destfile = str_replace($source, $dest, $file);
|
||||
|
||||
if (is_dir($file)) {
|
||||
if (!is_dir($destfile)) {
|
||||
// TODO "mkdir -p" here? (only natively supported by PHP 5)
|
||||
if (!@mkdir($destfile)) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!@copy($file, $destfile)) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$query = "INSERT INTO {$this->db_prefix}properties
|
||||
SELECT *
|
||||
FROM {$this->db_prefix}properties
|
||||
WHERE path = '".$options['path']."'";
|
||||
}
|
||||
|
||||
return ($new && !$existing_col) ? "201 Created" : "204 No Content";
|
||||
}
|
||||
|
||||
/**
|
||||
* PROPPATCH method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function PROPPATCH(&$options)
|
||||
{
|
||||
global $prefs, $tab;
|
||||
|
||||
$msg = "";
|
||||
$path = $options["path"];
|
||||
$dir = dirname($path)."/";
|
||||
$base = basename($path);
|
||||
|
||||
foreach ($options["props"] as $key => $prop) {
|
||||
if ($prop["ns"] == "DAV:") {
|
||||
$options["props"][$key]['status'] = "403 Forbidden";
|
||||
} else {
|
||||
if (isset($prop["val"])) {
|
||||
$query = "REPLACE INTO {$this->db_prefix}properties
|
||||
SET path = '$options[path]'
|
||||
, name = '$prop[name]'
|
||||
, ns= '$prop[ns]'
|
||||
, value = '$prop[val]'";
|
||||
} else {
|
||||
$query = "DELETE FROM {$this->db_prefix}properties
|
||||
WHERE path = '$options[path]'
|
||||
AND name = '$prop[name]'
|
||||
AND ns = '$prop[ns]'";
|
||||
}
|
||||
mysql_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LOCK method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function LOCK(&$options)
|
||||
{
|
||||
// get absolute fs path to requested resource
|
||||
$fspath = $this->base . $options["path"];
|
||||
|
||||
// TODO recursive locks on directories not supported yet
|
||||
if (is_dir($fspath) && !empty($options["depth"])) {
|
||||
return "409 Conflict";
|
||||
}
|
||||
|
||||
$options["timeout"] = time()+300; // 5min. hardcoded
|
||||
|
||||
if (isset($options["update"])) { // Lock Update
|
||||
$where = "WHERE path = '$options[path]' AND token = '$options[update]'";
|
||||
|
||||
$query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where";
|
||||
$res = mysql_query($query);
|
||||
$row = mysql_fetch_assoc($res);
|
||||
mysql_free_result($res);
|
||||
|
||||
if (is_array($row)) {
|
||||
$query = "UPDATE {$this->db_prefix}locks
|
||||
SET expires = '$options[timeout]'
|
||||
, modified = ".time()."
|
||||
$where";
|
||||
mysql_query($query);
|
||||
|
||||
$options['owner'] = $row['owner'];
|
||||
$options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
|
||||
$options['type'] = $row["exclusivelock"] ? "write" : "read";
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$query = "INSERT INTO {$this->db_prefix}locks
|
||||
SET token = '$options[locktoken]'
|
||||
, path = '$options[path]'
|
||||
, created = ".time()."
|
||||
, modified = ".time()."
|
||||
, owner = '$options[owner]'
|
||||
, expires = '$options[timeout]'
|
||||
, exclusivelock = " .($options['scope'] === "exclusive" ? "1" : "0")
|
||||
;
|
||||
mysql_query($query);
|
||||
|
||||
return mysql_affected_rows() ? "200 OK" : "409 Conflict";
|
||||
}
|
||||
|
||||
/**
|
||||
* UNLOCK method handler
|
||||
*
|
||||
* @param array general parameter passing array
|
||||
* @return bool true on success
|
||||
*/
|
||||
function UNLOCK(&$options)
|
||||
{
|
||||
$query = "DELETE FROM {$this->db_prefix}locks
|
||||
WHERE path = '$options[path]'
|
||||
AND token = '$options[token]'";
|
||||
mysql_query($query);
|
||||
|
||||
return mysql_affected_rows() ? "204 No Content" : "409 Conflict";
|
||||
}
|
||||
|
||||
/**
|
||||
* checkLock() helper
|
||||
*
|
||||
* @param string resource path to check for locks
|
||||
* @return bool true on success
|
||||
*/
|
||||
function checkLock($path)
|
||||
{
|
||||
$result = false;
|
||||
|
||||
$query = "SELECT owner, token, created, modified, expires, exclusivelock
|
||||
FROM {$this->db_prefix}locks
|
||||
WHERE path = '$path'
|
||||
";
|
||||
$res = mysql_query($query);
|
||||
|
||||
if ($res) {
|
||||
$row = mysql_fetch_array($res);
|
||||
mysql_free_result($res);
|
||||
|
||||
if ($row) {
|
||||
$result = array( "type" => "write",
|
||||
"scope" => $row["exclusivelock"] ? "exclusive" : "shared",
|
||||
"depth" => 0,
|
||||
"owner" => $row['owner'],
|
||||
"token" => $row['token'],
|
||||
"created" => $row['created'],
|
||||
"modified" => $row['modified'],
|
||||
"expires" => $row['expires']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create database tables for property and lock storage
|
||||
*
|
||||
* @param void
|
||||
* @return bool true on success
|
||||
*/
|
||||
function create_database()
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode:nil
|
||||
* End:
|
||||
*/
|
538
etemplate/inc/class.vfs_widget.inc.php
Normal file
538
etemplate/inc/class.vfs_widget.inc.php
Normal file
@ -0,0 +1,538 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare eTemplate Extension - VFS Widgets
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @author Ralf Becker <RalfBecker@outdoor-training.de>
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @copyright 2008-10 by RalfBecker@outdoor-training.de
|
||||
* @package etemplate
|
||||
* @subpackage extensions
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* eTemplate extension to display stuff from the VFS system
|
||||
*
|
||||
* Contains the following widgets:
|
||||
* - vfs aka File name+link: clickable filename, with evtl. clickable path-components
|
||||
* - vfs-name aka Filename: filename automatically urlencoded on return (urldecoded on display to user)
|
||||
* - vfs-size aka File size: human readable filesize, eg. 1.4k
|
||||
* - vfs-mode aka File mode: posix mode as string eg. drwxr-x---
|
||||
* - vfs-mime aka File icon: mime type icon or thumbnail (if configured AND enabled in the user-prefs)
|
||||
* - vfs-uid aka File owner: Owner of file, or 'root' if none
|
||||
* - vfs-gid aka File group: Group of file, or 'root' if none
|
||||
* - vfs-upload aka VFS file: displays either download and delete (x) links or a file upload
|
||||
* + value is either a vfs path or colon separated $app:$id:$relative_path, eg: infolog:123:special/offer
|
||||
* + if empty($id) / new entry, file is created in a hidden temporary directory in users home directory
|
||||
* and calling app is responsible to move content of that dir to entry directory, after entry is saved
|
||||
* + option: required mimetype or regular expression for mimetype to match, eg. '/^text\//i' for all text files
|
||||
* + if path ends in a slash, multiple files can be uploaded, their original filename is kept then
|
||||
*
|
||||
* All widgets accept as value a full path.
|
||||
* vfs-mime and vfs itself also allow an array with values like stat (incl. 'path'!) as value.
|
||||
* vfs-mime also allows just the mime type as value.
|
||||
* All other widgets allow additionally the nummeric value from the stat call (to not call it again).
|
||||
*/
|
||||
class vfs_widget
|
||||
{
|
||||
/**
|
||||
* exported methods of this class
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $public_functions = array(
|
||||
'pre_process' => True,
|
||||
'post_process' => true, // post_process is only used for vfs-upload (all other widgets set $cell['readlonly']!)
|
||||
);
|
||||
/**
|
||||
* availible extensions and there names for the editor
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $human_name = array(
|
||||
'vfs' => 'File name+link', // clickable filename, with evtl. clickable path-components
|
||||
'vfs-name' => 'File name', // filename automatically urlencoded
|
||||
'vfs-size' => 'File size', // human readable filesize
|
||||
'vfs-mode' => 'File mode', // posix mode as string eg. drwxr-x---
|
||||
'vfs-mime' => 'File icon', // mime type icon or thumbnail
|
||||
'vfs-uid' => 'File owner', // Owner of file, or 'root' if none
|
||||
'vfs-gid' => 'File group', // Group of file, or 'root' if none
|
||||
'vfs-upload' => 'VFS file', // displays either download and delete (x) links or a file upload
|
||||
);
|
||||
|
||||
/**
|
||||
* pre-processing of the extension
|
||||
*
|
||||
* This function is called before the extension gets rendered
|
||||
*
|
||||
* @param string $form_name form-name of the control
|
||||
* @param mixed &$value value / existing content, can be modified
|
||||
* @param array &$cell array with the widget, can be modified for ui-independent widgets
|
||||
* @param array &$readonlys names of widgets as key, to be made readonly
|
||||
* @param mixed &$extension_data data the extension can store persisten between pre- and post-process
|
||||
* @param object &$tmpl reference to the template we belong too
|
||||
* @return boolean true if extra label is allowed, false otherwise
|
||||
*/
|
||||
function pre_process($form_name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
|
||||
{
|
||||
//echo "<p>".__METHOD__."($form_name,$value,".array2string($cell).",...)</p>\n";
|
||||
$type = $cell['type'];
|
||||
if (!in_array($type,array('vfs-name','vfs-upload'))) $cell['readonly'] = true; // to not call post-process
|
||||
|
||||
// check if we have a path and not the raw value, in that case we have to do a stat first
|
||||
if (in_array($type,array('vfs-size','vfs-mode','vfs-uid','vfs-gid')) && !is_numeric($value) || $type == 'vfs' && !$value)
|
||||
{
|
||||
if (!$value || !($stat = egw_vfs::stat($value)))
|
||||
{
|
||||
if ($value) $value = lang("File '%1' not found!",egw_vfs::decodePath($value));
|
||||
$cell = etemplate::empty_cell();
|
||||
return true; // allow extra value;
|
||||
}
|
||||
}
|
||||
$cell['type'] = 'label';
|
||||
|
||||
switch($type)
|
||||
{
|
||||
case 'vfs-upload': // option: required mimetype or regular expression for mimetype to match, eg. '/^text\//i' for all text files
|
||||
if (empty($value) && preg_match('/^exec.*\[([^]]+)\]$/',$form_name,$matches)) // if no value via content array, use widget name
|
||||
{
|
||||
$value = $matches[1];
|
||||
}
|
||||
$extension_data = array('value' => $value, 'mimetype' => $cell['size'], 'type' => $type);
|
||||
if ($value[0] != '/')
|
||||
{
|
||||
list($app,$id,$relpath) = explode(':',$value,3);
|
||||
if (empty($id))
|
||||
{
|
||||
static $tmppath = array(); // static var, so all vfs-uploads get created in the same temporary dir
|
||||
if (!isset($tmppath[$app])) $tmppath[$app] = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.'.$app.'_'.md5(time().session_id());
|
||||
$value = $tmppath[$app];
|
||||
unset($cell['onchange']); // no onchange, if we have to use a temporary dir
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = egw_link::vfs_path($app,$id,'',true);
|
||||
}
|
||||
if (!empty($relpath)) $value .= '/'.$relpath;
|
||||
}
|
||||
$path = $extension_data['path'] = $value;
|
||||
if (substr($path,-1) != '/' && self::file_exists($path) && !egw_vfs::is_dir($path)) // display download link and delete icon
|
||||
{
|
||||
$extension_data['path'] = $path;
|
||||
$cell = $this->file_widget($value,$path,$cell['name'],$cell['label']);
|
||||
}
|
||||
else // file does NOT exists --> display file upload
|
||||
{
|
||||
$cell['type'] = 'file';
|
||||
// if no explicit help message set and we only allow certain file types --> show them
|
||||
if (empty($cell['help']) && $cell['size'])
|
||||
{
|
||||
if (($type = mime_magic::mime2ext($cell['size'])))
|
||||
{
|
||||
$type = '*.'.strtoupper($type);
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = $cell['size'];
|
||||
}
|
||||
$cell['help'] = lang('Allowed file type: %1',$type);
|
||||
}
|
||||
}
|
||||
// check if directory (trailing slash) is given --> upload of multiple files
|
||||
if (substr($path,-1) == '/' && egw_vfs::file_exists($path) && ($files = egw_vfs::scandir($path)))
|
||||
{
|
||||
//echo $path; _debug_array($files);
|
||||
$upload = $cell;
|
||||
$cell = etemplate::empty_cell('vbox','',array('size' => ',,0,0'));
|
||||
$extension_data['files'] = $files;
|
||||
$value = array();
|
||||
foreach($files as $file)
|
||||
{
|
||||
$file = $path.$file;
|
||||
$basename = basename($file);
|
||||
unset($widget);
|
||||
$widget = $this->file_widget($value[$basename],$file,$upload['name']."[$basename]");
|
||||
etemplate::add_child($cell,$widget);
|
||||
}
|
||||
etemplate::add_child($cell,$upload);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'vfs-size': // option: add size in bytes in brackets
|
||||
$value = egw_vfs::hsize($size = is_numeric($value) ? $value : $stat['size']);
|
||||
if ($cell['size']) $value .= ' ('.$size.')';
|
||||
$cell['type'] = 'label';
|
||||
break;
|
||||
|
||||
case 'vfs-mode':
|
||||
$value = egw_vfs::int2mode(is_numeric($value) ? $value : $stat['mode']);
|
||||
list($span,$class) = explode(',',$cell['span'],2);
|
||||
$class .= ($class ? ' ' : '') . 'vfsMode';
|
||||
$cell['span'] = $span.','.$class;
|
||||
$cell['no_lang'] = true;
|
||||
break;
|
||||
|
||||
case 'vfs-uid':
|
||||
case 'vfs-gid':
|
||||
$uid = !is_numeric($value) ? $stat[$type=='vfs-uid'?'uid':'gid'] : $value;
|
||||
$value = !$uid ? 'root' : $GLOBALS['egw']->accounts->id2name($type=='vfs-uid'?$uid:-$uid); // our internal gid's are negative!
|
||||
break;
|
||||
|
||||
case 'vfs':
|
||||
if (is_array($value))
|
||||
{
|
||||
$name = $value['name'];
|
||||
$path = substr($value['path'],0,-strlen($name)-1);
|
||||
$mime = $value['mime'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = $value;
|
||||
$path = '';
|
||||
$mime = egw_vfs::mime_content_type($value);
|
||||
$value = array();
|
||||
}
|
||||
if (($cell_name = $cell['name']) == '$row')
|
||||
{
|
||||
$cell_name = array_pop($arr=explode('][',substr($form_name,0,-1)));
|
||||
}
|
||||
$cell['name'] = '';
|
||||
$cell['type'] = 'hbox';
|
||||
$cell['size'] = '0,,0,0';
|
||||
foreach($name != '/' ? explode('/',$name) : array('') as $n => $component)
|
||||
{
|
||||
if ($n > (int)($path === '/'))
|
||||
{
|
||||
$sep = soetemplate::empty_cell('label','',array('label' => '/'));
|
||||
soetemplate::add_child($cell,$sep);
|
||||
unset($sep);
|
||||
}
|
||||
$value['c'.$n] = $component !== '' ? egw_vfs::decodePath($component) : '/';
|
||||
$path .= ($path != '/' ? '/' : '').$component;
|
||||
// replace id's in /apps again with human readable titles
|
||||
$path_parts = explode('/',$path);
|
||||
if ($path_parts[1] == 'apps')
|
||||
{
|
||||
switch(count($path_parts))
|
||||
{
|
||||
case 2:
|
||||
$value['c'.$n] = lang('Applications');
|
||||
break;
|
||||
case 3:
|
||||
$value['c'.$n] = lang($path_parts[2]);
|
||||
break;
|
||||
case 4:
|
||||
if (is_numeric($value['c'.$n])) $value['c'.$n] .= egw_link::title($path_parts[2],$path_parts[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (egw_vfs::is_readable($path)) // show link only if we have access to the file or dir
|
||||
{
|
||||
if ($n < count($comps)-1 || $mime == egw_vfs::DIR_MIME_TYPE || egw_vfs::is_dir($path))
|
||||
{
|
||||
$value['l'.$n] = '/index.php?menuaction=filemanager.filemanager_ui.index&path='.urlencode($path);
|
||||
$target = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$value['l'.$n] = egw_vfs::download_url($path);
|
||||
$target = ',,,_blank';
|
||||
}
|
||||
}
|
||||
|
||||
if ($cell['onclick'])
|
||||
{
|
||||
$comp = etemplate::empty_cell('button',$cell_name.'[c'.$n.']',array(
|
||||
'size' => '1',
|
||||
'no_lang' => true,
|
||||
'span' => ',vfsFilename',
|
||||
'label' => $value['c'.$n],
|
||||
'onclick' => str_replace('$path',"'".addslashes($path)."'",$cell['onclick']),
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
$comp = etemplate::empty_cell('label',$cell_name.'[c'.$n.']',array(
|
||||
'size' => ',@'.$cell_name.'[l'.$n.']'.$target,
|
||||
'no_lang' => true,
|
||||
'span' => ',vfsFilename',
|
||||
));
|
||||
}
|
||||
etemplate::add_child($cell,$comp);
|
||||
unset($comp);
|
||||
}
|
||||
unset($cell['onclick']); // otherwise it's handled by the grid too
|
||||
//_debug_array($comps); _debug_array($cell); _debug_array($value);
|
||||
break;
|
||||
|
||||
case 'vfs-name': // size: [length][,maxLength[,allowPath]]
|
||||
$cell['type'] = 'text';
|
||||
list($length,$maxLength,$allowPath) = $options = explode(',',$cell['size']);
|
||||
$preg = $allowPath ? '' : '/[^\\/]/'; // no slash '/' allowed, if not allowPath set
|
||||
$cell['size'] = "$length,$maxLength,$preg";
|
||||
$value = egw_vfs::decodePath($value);
|
||||
$extension_data = array('type' => $type,'allowPath' => $allowPath);
|
||||
break;
|
||||
|
||||
case 'vfs-mime':
|
||||
if (!$value)
|
||||
{
|
||||
$cell = etemplate::empty_cell();
|
||||
return true;
|
||||
}
|
||||
if (!is_array($value))
|
||||
{
|
||||
if ($value[0] == '/' || count(explode('/',$value)) != 2)
|
||||
{
|
||||
$mime = egw_vfs::mime_content_type($path=$value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$mime = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = $value['path'];
|
||||
$mime = $value['mime'];
|
||||
}
|
||||
//error_log(__METHOD__."() type=vfs-mime: value=".array2string($value).": mime=$mime, path=$path");
|
||||
$cell['type'] = 'image';
|
||||
$cell['label'] = mime_magic::mime2label($mime);
|
||||
|
||||
list($mime_main,$mime_sub) = explode('/',$mime);
|
||||
if ($mime_main == 'egw')
|
||||
{
|
||||
$value = $mime_sub.'/navbar'; // egw-applications for link-widget
|
||||
$cell['label'] = lang($mime_sub);
|
||||
list($span,$class) = explode(',',$cell['span'],2);
|
||||
$class .= ($class ? ' ' : '') . 'vfsMimeIcon';
|
||||
$cell['span'] = $span.','.$class;
|
||||
}
|
||||
elseif($path && $mime_main == 'image' && in_array($mime_sub,array('png','jpeg','jpg','gif','bmp')) &&
|
||||
(string)$GLOBALS['egw_info']['server']['link_list_thumbnail'] != '0' &&
|
||||
(string)$GLOBALS['egw_info']['user']['preferences']['common']['link_list_thumbnail'] != '0' &&
|
||||
// check the size of the image, as too big images get no icon, but a PHP Fatal error: Allowed memory size exhausted
|
||||
(!is_array($value) && ($stat = egw_vfs::stat($path)) ? $stat['size'] : $value['size']) < 600000)
|
||||
{
|
||||
if (substr($path,0,6) == '/apps/')
|
||||
{
|
||||
$path = parse_url(egw_vfs::resolve_url_symlinks($path),PHP_URL_PATH);
|
||||
}
|
||||
$value = $GLOBALS['egw']->link('/etemplate/thumbnail.php',array('path' => $path));
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = egw_vfs::mime_icon($mime);
|
||||
}
|
||||
// mark symlinks (check if method exists, to allow etemplate to run on 1.6 API!)
|
||||
if (method_exists('egw_vfs','is_link') && egw_vfs::is_link($path))
|
||||
{
|
||||
$broken = !egw_vfs::stat($path);
|
||||
list($span,$class) = explode(',',$cell['span'],2);
|
||||
$class .= ($class ? ' ' : '') . ($broken ? 'vfsIsBrokenLink' : 'vfsIsLink');
|
||||
$cell['span'] = $span.','.$class;
|
||||
$cell['label'] = ($broken ? lang('Broken link') : lang('Link')).': '.egw_vfs::decodePath(egw_vfs::readlink($path)).
|
||||
(!$broken ? ' ('.$cell['label'].')' : '');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$value = 'Not yet implemented';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create widget with download and delete (only if dir is writable) link
|
||||
*
|
||||
* @param mixed &$value
|
||||
* @param string $path vfs path of download
|
||||
* @param string $name name of widget
|
||||
* @param string $label=null label, if not set basename($path) is used
|
||||
* @return array
|
||||
*/
|
||||
static function file_widget(&$value,$path,$name,$label=null)
|
||||
{
|
||||
$value = empty($label) ? egw_vfs::decodePath(egw_vfs::basename($path)) : lang($label); // display (translated) Label or filename (if label empty)
|
||||
|
||||
$vfs_link = etemplate::empty_cell('label',$name,array(
|
||||
'size' => ','.egw_vfs::download_url($path).',,,_blank,,'.$path,
|
||||
));
|
||||
// if dir is writable, add delete link
|
||||
if (egw_vfs::is_writable(egw_vfs::dirname($path)))
|
||||
{
|
||||
$cell = etemplate::empty_cell('hbox','',array('size' => ',,0,0'));
|
||||
etemplate::add_child($cell,$vfs_link);
|
||||
$delete_icon = etemplate::empty_cell('button',$path,array(
|
||||
'label' => 'delete',
|
||||
'size' => 'delete', // icon
|
||||
'onclick' => "return confirm('Delete this file');",
|
||||
'span' => ',leftPad5',
|
||||
));
|
||||
etemplate::add_child($cell,$delete_icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
$cell = $vfs_link;
|
||||
}
|
||||
return $cell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if vfs file exists *without* using the extension
|
||||
*
|
||||
* If you rename a file, you have to clear the cache ($clear_after=true)!
|
||||
*
|
||||
* @param string &$path on call path without extension, if existing on return full path incl. extension
|
||||
* @param boolean $clear_after=null clear file-cache after (true) or before (false), default dont clear
|
||||
* @return
|
||||
*/
|
||||
static function file_exists(&$path,$clear_after=null)
|
||||
{
|
||||
static $files = array(); // static var, to scan each directory only once
|
||||
$dir = egw_vfs::dirname($path);
|
||||
if ($clear_after === false) unset($files[$dir]);
|
||||
if (!isset($files[$dir])) $files[$dir] = egw_vfs::file_exists($dir) ? egw_vfs::scandir($dir) : array();
|
||||
|
||||
$basename = egw_vfs::basename($path);
|
||||
$basename_len = strlen($basename);
|
||||
$found = false;
|
||||
foreach($files[$dir] as $file)
|
||||
{
|
||||
if (substr($file,0,$basename_len) == $basename)
|
||||
{
|
||||
$path = $dir.'/'.$file;
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
if ($clear_after === true) unset($files[$dir]);
|
||||
//echo "<p>".__METHOD__."($path) returning ".array2string($found)."</p>\n";
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* postprocessing method, called after the submission of the form
|
||||
*
|
||||
* It has to copy the allowed/valid data from $value_in to $value, otherwise the widget
|
||||
* will return no data (if it has a preprocessing method). The framework insures that
|
||||
* the post-processing of all contained widget has been done before.
|
||||
*
|
||||
* Only used by vfs-upload so far
|
||||
*
|
||||
* @param string $name form-name of the widget
|
||||
* @param mixed &$value the extension returns here it's input, if there's any
|
||||
* @param mixed &$extension_data persistent storage between calls or pre- and post-process
|
||||
* @param boolean &$loop can be set to true to request a re-submision of the form/dialog
|
||||
* @param object &$tmpl the eTemplate the widget belongs too
|
||||
* @param mixed &value_in the posted values (already striped of magic-quotes)
|
||||
* @return boolean true if $value has valid content, on false no content will be returned!
|
||||
*/
|
||||
function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in)
|
||||
{
|
||||
//error_log(__METHOD__."('$name',".array2string($value).','.array2string($extension_data).",$loop,,".array2string($value_in).')');
|
||||
//echo '<p>'.__METHOD__."('$name',".array2string($value).','.array2string($extension_data).",$loop,,".array2string($value_in).")</p>\n";
|
||||
|
||||
if (!$extension_data) return false;
|
||||
|
||||
switch($extension_data['type'])
|
||||
{
|
||||
case 'vfs-name':
|
||||
$value = $extension_data['allowPath'] ? egw_vfs::encodePath($value_in) : egw_vfs::encodePathComponent($value_in);
|
||||
return true;
|
||||
|
||||
case 'vfs-upload':
|
||||
break; // handeled below
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// from here on vfs-upload only!
|
||||
|
||||
// check if delete icon clicked
|
||||
if ($_POST['submit_button'] == ($fname = str_replace($extension_data['value'],$extension_data['path'],$name)) ||
|
||||
substr($extension_data['path'],-1) == '/' && substr($_POST['submit_button'],0,strlen($fname)-1) == substr($fname,0,-1))
|
||||
{
|
||||
if (substr($extension_data['path'],-1) == '/') // multiple files?
|
||||
{
|
||||
foreach($extension_data['files'] as $file) // check of each single file, to not allow deleting of arbitrary files
|
||||
{
|
||||
if ($_POST['submit_button'] == substr($fname,0,-1).$file.']')
|
||||
{
|
||||
if (!egw_vfs::unlink($extension_data['path'].$file))
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('Error deleting %1!',egw_vfs::decodePath($extension_data['path'].$file)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (!egw_vfs::unlink($extension_data['path']))
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('Error deleting %1!',egw_vfs::decodePath($extension_data['path'])));
|
||||
}
|
||||
$loop = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle file upload
|
||||
$name = preg_replace('/^exec\[([^]]+)\](.*)$/','\\1\\2',$name); // remove exec prefix
|
||||
|
||||
if (!is_array($_FILES['exec']) || !($filename = etemplate::get_array($_FILES['exec']['name'],$name)))
|
||||
{
|
||||
return false; // no file attached
|
||||
}
|
||||
$tmp_name = etemplate::get_array($_FILES['exec']['tmp_name'],$name);
|
||||
$error = etemplate::get_array($_FILES['exec']['error'],$name);
|
||||
if ($error)
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('Error uploading file!')."\n".
|
||||
etemplate::max_upload_size_message());
|
||||
$loop = true;
|
||||
return false;
|
||||
}
|
||||
if (empty($tmp_name) || function_exists('is_uploaded_file') && !is_uploaded_file($tmp_name) || !file_exists($tmp_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// check if type matches required mime-type, if specified
|
||||
if (!empty($extension_data['mimetype']))
|
||||
{
|
||||
$type = etemplate::get_array($_FILES['exec']['type'],$name);
|
||||
$is_preg = $extension_data['mimetype'][0] == '/';
|
||||
if (!$is_preg && strcasecmp($extension_data['mimetype'],$type) || $is_preg && !preg_match($extension_data['mimetype'],$type))
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('File is of wrong type (%1 != %2)!',$type,$extension_data['mimetype']));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$path = $extension_data['path'];
|
||||
if (substr($path,-1) != '/')
|
||||
{
|
||||
// add extension to path
|
||||
$parts = explode('.',$filename);
|
||||
if (($extension = array_pop($parts)) && mime_magic::ext2mime($extension)) // really an extension --> add it to path
|
||||
{
|
||||
$path .= '.'.$extension;
|
||||
}
|
||||
}
|
||||
else // multiple upload with dir given (trailing slash)
|
||||
{
|
||||
$path .= egw_vfs::encodePathComponent($filename);
|
||||
}
|
||||
if (!egw_vfs::file_exists($dir = egw_vfs::dirname($path)) && !egw_vfs::mkdir($dir,null,STREAM_MKDIR_RECURSIVE))
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('Error create parent directory %1!',egw_vfs::decodePath($dir)));
|
||||
return false;
|
||||
}
|
||||
if (!copy($tmp_name,egw_vfs::PREFIX.$path))
|
||||
{
|
||||
etemplate::set_validation_error($name,lang('Error copying uploaded file to vfs!'));
|
||||
return false;
|
||||
}
|
||||
$value = $path; // return path of file, important if only a temporary location is used
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -127,7 +127,7 @@ class filemanager_ui
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= lang('The requested path %1 is not available.',urldecode($path));
|
||||
$msg .= lang('The requested path %1 is not available.',egw_vfs::decodePath($path));
|
||||
}
|
||||
// reset lettersearch as it confuses users (they think the dir is empty)
|
||||
$content['nm']['searchletter'] = false;
|
||||
@ -205,11 +205,11 @@ class filemanager_ui
|
||||
$abs_target = $target[0] == '/' ? $target : egw_vfs::concat($content['nm']['path'],$target);
|
||||
if (!egw_vfs::stat($abs_target))
|
||||
{
|
||||
$content['nm']['msg'] = lang('Link target %1 not found!',urldecode($abs_target));
|
||||
$content['nm']['msg'] = lang('Link target %1 not found!',egw_vfs::decodePath($abs_target));
|
||||
break;
|
||||
}
|
||||
$content['nm']['msg'] = egw_vfs::symlink($target,$link) ?
|
||||
lang('Symlink to %1 created.',$target) : lang('Error creating symlink to target %1!',urldecode($target));
|
||||
lang('Symlink to %1 created.',$target) : lang('Error creating symlink to target %1!',egw_vfs::decodePath($target));
|
||||
break;
|
||||
case 'paste':
|
||||
$content['nm']['msg'] = self::action($clipboard_type.'_paste',$clipboard_files,$content['nm']['path']);
|
||||
@ -244,7 +244,7 @@ class filemanager_ui
|
||||
if ($upload_success)
|
||||
{
|
||||
$content['nm']['msg'] = count($upload_success) == 1 && !$upload_failure ? lang('File successful uploaded.') :
|
||||
lang('%1 successful uploaded.',urldecode(implode(', ',$upload_success)));
|
||||
lang('%1 successful uploaded.',implode(', ',$upload_success));
|
||||
}
|
||||
if ($upload_failure)
|
||||
{
|
||||
@ -262,9 +262,9 @@ class filemanager_ui
|
||||
$dir_is_writable = egw_vfs::is_writable($content['nm']['path']);
|
||||
}
|
||||
$content['paste_tooltip'] = $clipboard_files ? '<p><b>'.lang('%1 the following files into current directory',
|
||||
$clipboard_type=='copy'?lang('Copy'):lang('Move')).':</b><br />'.urldecode(implode('<br />',$clipboard_files)).'</p>' : '';
|
||||
$clipboard_type=='copy'?lang('Copy'):lang('Move')).':</b><br />'.egw_vfs::decodePath(implode('<br />',$clipboard_files)).'</p>' : '';
|
||||
$content['linkpaste_tooltip'] = $clipboard_files ? '<p><b>'.lang('%1 the following files into current directory',
|
||||
lang('link')).':</b><br />'.urldecode(implode('<br />',$clipboard_files)).'</p>' : '';
|
||||
lang('link')).':</b><br />'.egw_vfs::decodePath(implode('<br />',$clipboard_files)).'</p>' : '';
|
||||
$content['upload_size'] = etemplate::max_upload_size_message();
|
||||
//_debug_array($content);
|
||||
|
||||
@ -320,7 +320,7 @@ class filemanager_ui
|
||||
}
|
||||
else
|
||||
{
|
||||
$response->addScript("if (!confirm('".addslashes(lang('Do you want to overwrite the existing file %1?',urldecode($path)))."')) document.getElementById('$id').value='';");
|
||||
$response->addScript("if (!confirm('".addslashes(lang('Do you want to overwrite the existing file %1?',egw_vfs::decodePath($path)))."')) document.getElementById('$id').value='';");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -498,7 +498,7 @@ class filemanager_ui
|
||||
{
|
||||
if (preg_match('/^\/?(home|apps|)\/*$/',$path))
|
||||
{
|
||||
return lang("Cautiously rejecting to remove folder '%1'!",urldecode($path));
|
||||
return lang("Cautiously rejecting to remove folder '%1'!",egw_vfs::decodePath($path));
|
||||
}
|
||||
}
|
||||
// now we use find to loop through all files and dirs: (selected only contains dirs now)
|
||||
@ -558,7 +558,7 @@ class filemanager_ui
|
||||
// an appropriate message
|
||||
egw::redirect_link('/index.php',array('menuaction'=>'filemanager.filemanager_ui.index',
|
||||
'path' => self::get_home_dir(),
|
||||
'msg' => lang('The requested path %1 is not available.',urldecode($query['path'])),
|
||||
'msg' => lang('The requested path %1 is not available.',egw_vfs::decodePath($query['path'])),
|
||||
));
|
||||
}
|
||||
$rows = $dir_is_writable = array();
|
||||
@ -639,7 +639,7 @@ class filemanager_ui
|
||||
}
|
||||
else
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Filemanager').': '.urldecode($query['path']);
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Filemanager').': '.egw_vfs::decodePath($query['path']);
|
||||
}
|
||||
return egw_vfs::$find_total;
|
||||
}
|
||||
@ -737,14 +737,14 @@ class filemanager_ui
|
||||
}
|
||||
if (egw_vfs::rename($path,$to))
|
||||
{
|
||||
$msg .= lang('Renamed %1 to %2.',urldecode(basename($path)),urldecode(basename($to))).' ';
|
||||
$msg .= lang('Renamed %1 to %2.',egw_vfs::decodePath(basename($path)),egw_vfs::decodePath(basename($to))).' ';
|
||||
$content['old']['name'] = $content[$name];
|
||||
$path = $to;
|
||||
$content['mime'] = mime_magic::filename2mime($path); // recheck mime type
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= lang('Rename of %1 to %2 failed!',urldecode(basename($path)),urldecode(basename($to))).' ';
|
||||
$msg .= lang('Rename of %1 to %2 failed!',egw_vfs::decodePath(basename($path)),egw_vfs::decodePath(basename($to))).' ';
|
||||
if (egw_vfs::deny_script($to))
|
||||
{
|
||||
$msg .= lang('You are NOT allowed to upload a script!').' ';
|
||||
@ -942,7 +942,7 @@ class filemanager_ui
|
||||
));
|
||||
}
|
||||
$GLOBALS['egw_info']['flags']['java_script'] = "<script>window.focus();</script>\n";
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Preferences').' '.urldecode($path);
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Preferences').' '.egw_vfs::decodePath($path);
|
||||
|
||||
$tpl->exec('filemanager.filemanager_ui.file',$content,$sel_options,$readonlys,$preserve,2);
|
||||
}
|
||||
|
1149
phpgwapi/inc/class.egw_link.inc.php
Normal file
1149
phpgwapi/inc/class.egw_link.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -1365,7 +1365,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
*
|
||||
* Not all chars get encoded, slashes '/' are silently removed!
|
||||
*
|
||||
* To reverse the encoding, eg. to display a filename to the user, you can use urldecode()
|
||||
* To reverse the encoding, eg. to display a filename to the user, you have to use egw_vfs::decodePath()
|
||||
*
|
||||
* @param string|array $component
|
||||
* @return string|array
|
||||
@ -1378,7 +1378,7 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
/**
|
||||
* Encode a path: replacing certain chars with their urlencoded counterparts
|
||||
*
|
||||
* To reverse the encoding, eg. to display a filename to the user, you can use urldecode()
|
||||
* To reverse the encoding, eg. to display a filename to the user, you have to use egw_vfs::decodePath()
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
@ -1388,6 +1388,19 @@ class egw_vfs extends vfs_stream_wrapper
|
||||
return implode('/',self::encodePathComponent(explode('/',$path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a path: rawurldecode(): mostly urldecode(), but do NOT decode '+', as we're NOT encoding it!
|
||||
*
|
||||
* Used eg. to translate a path for displaying to the User.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
static public function decodePath($path)
|
||||
{
|
||||
return rawurldecode($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise our static vars
|
||||
*/
|
||||
|
@ -153,7 +153,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
}
|
||||
|
||||
// open the "real" file
|
||||
if (!($this->opened_stream = fopen($path=urldecode(parse_url($url,PHP_URL_PATH)),$mode,$options)))
|
||||
if (!($this->opened_stream = fopen($path=egw_vfs::decodePath(parse_url($url,PHP_URL_PATH)),$mode,$options)))
|
||||
{
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url,$mode,$options) fopen('$path','$mode',$options) returned false!");
|
||||
return false;
|
||||
@ -295,7 +295,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static function unlink ( $url )
|
||||
{
|
||||
$path = urldecode(parse_url($url,PHP_URL_PATH));
|
||||
$path = egw_vfs::decodePath(parse_url($url,PHP_URL_PATH));
|
||||
|
||||
// check access rights (file need to exist and directory need to be writable
|
||||
if (!file_exists($path) || is_dir($path) || !egw_vfs::check_access(egw_vfs::dirname($url),egw_vfs::WRITABLE))
|
||||
@ -355,7 +355,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
if (self::LOG_LEVEL) error_log(__METHOD__."($url_to,$url_from) can't unlink existing $url_to!");
|
||||
return false;
|
||||
}
|
||||
return rename(urldecode($from['path']),urldecode($to['path']));
|
||||
return rename(egw_vfs::decodePath($from['path']),egw_vfs::decodePath($to['path']));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,7 +371,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static function mkdir ( $url, $mode, $options )
|
||||
{
|
||||
$path = urldecode(parse_url($url,PHP_URL_PATH));
|
||||
$path = egw_vfs::decodePath(parse_url($url,PHP_URL_PATH));
|
||||
$recursive = (bool)($options & STREAM_MKDIR_RECURSIVE);
|
||||
|
||||
// find the real parent (might be more then one level if $recursive!)
|
||||
@ -403,7 +403,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static function rmdir ( $url, $options )
|
||||
{
|
||||
$path = urldecode(parse_url($url,PHP_URL_PATH));
|
||||
$path = egw_vfs::decodePath(parse_url($url,PHP_URL_PATH));
|
||||
$parent = dirname($path);
|
||||
|
||||
// check access rights (in real filesystem AND by mount perms)
|
||||
@ -425,7 +425,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
*/
|
||||
static function touch($url,$time=null,$atime=null)
|
||||
{
|
||||
$path = urldecode(parse_url($url,PHP_URL_PATH));
|
||||
$path = egw_vfs::decodePath(parse_url($url,PHP_URL_PATH));
|
||||
$parent = dirname($path);
|
||||
|
||||
// check access rights (in real filesystem AND by mount perms)
|
||||
@ -492,7 +492,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
|
||||
$this->opened_dir = null;
|
||||
|
||||
$path = urldecode(parse_url($this->opened_dir_url = $url,PHP_URL_PATH));
|
||||
$path = egw_vfs::decodePath(parse_url($this->opened_dir_url = $url,PHP_URL_PATH));
|
||||
|
||||
// ToDo: check access rights
|
||||
|
||||
@ -533,7 +533,7 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
static function url_stat ( $url, $flags )
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
$path = urldecode($parts['path']);
|
||||
$path = egw_vfs::decodePath($parts['path']);
|
||||
|
||||
$stat = @stat($path); // suppressed the stat failed warnings
|
||||
|
||||
@ -733,20 +733,20 @@ class filesystem_stream_wrapper implements iface_stream_wrapper
|
||||
list(,$query) = explode('?',$url,2);
|
||||
parse_str($query,$get);
|
||||
if (empty($get['url'])) return false; // no download url given for this mount-point
|
||||
|
||||
|
||||
if (!($mount_url = egw_vfs::mount_url($url))) return false; // no mount url found, should not happen
|
||||
list($mount_url) = explode('?',$mount_url);
|
||||
|
||||
|
||||
list($url,$query) = explode('?',$url,2);
|
||||
$relpath = substr($url,strlen($mount_url));
|
||||
|
||||
|
||||
$download_url = egw_vfs::concat($get['url'],$relpath);
|
||||
if ($download_url[0] == '/')
|
||||
{
|
||||
$download_url = ($_SERVER['HTTPS'] ? 'https://' : 'http://').
|
||||
$_SERVER['HTTP_HOST'].$download_url;
|
||||
}
|
||||
|
||||
|
||||
//die(__METHOD__."('$url') --> relpath = $relpath --> $download_url");
|
||||
return $download_url;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user