special mkdir of links_stream_wrapper, so the entry dirs do not inherit the other read/browse rights from the app dir

This commit is contained in:
Ralf Becker 2008-04-21 11:06:45 +00:00
parent 2a1d86911b
commit 0080dc39f0
2 changed files with 100 additions and 77 deletions

View File

@ -17,11 +17,11 @@
/**
* Generalized linking between entries of eGroupware apps
*
*
* Please note: this class can NOT and does not need to be initialised, all methods are static
*
*
* To participate in the linking an applications has to implement the following hooks:
*
*
* /**
* * Hook called by link-class to include app in the appregistry of the linkage
* *
@ -38,13 +38,13 @@
* 'menuaction' => 'app.class.method',
* ),
* 'view_id' => 'app_id', // name of get parameter of the id
* 'view_popup' => '400x300', // size of popup (XxY), if view is in popup
* 'view_popup' => '400x300', // size of popup (XxY), if view is in popup
* 'add' => array( // get parameter to add an empty entry to app
* 'menuaction' => 'app.class.method',
* ),
* 'add_app' => 'link_app', // name of get parameter to add links to other app
* 'add_id' => 'link_id', // --------------------- " ------------------- id
* 'add_popup' => '400x300', // size of popup (XxY), if add is in popup
* 'add_popup' => '400x300', // size of popup (XxY), if add is in popup
* 'notify' => 'app.class.method', // method to be called if an other applications liks or unlinks with app: notify(array $data)
* 'file_access' => 'app.class.method', // method to be called to check file access rights, see links_stream_wrapper class
* ); // boolean file_access(string $id,int $check,string $rel_path)
@ -54,7 +54,7 @@
* The BO-layer implementes some extra features on top of the so-layer:
* 1) It handles links to not already existing entries. This is used by the eTemplate link-widget, which allows to
* setup links even for new / not already existing entries, before they get saved.
* In that case you have to set the first id to 0 for the link-static function and pass the array returned in that id
* In that case you have to set the first id to 0 for the link-static function and pass the array returned in that id
* (not the return-value) after saveing your new entry again to the link static function.
* 2) Attaching files: they are saved in the vfs and not the link-table (!).
* Attached files are stored under $vfs_basedir='/infolog' in the vfs!
@ -74,7 +74,7 @@ class egw_link extends solink
/**
* other apps can participate in the linking by implementing a 'search_link' hook, which
* has to return an array in the format of an app_register entry below
*
*
* @var array
*/
static $app_register = array(
@ -98,7 +98,7 @@ class egw_link extends solink
*/
private function __construct()
{
}
/**
@ -129,7 +129,7 @@ class egw_link extends solink
self::$title_cache = array();
}
}
/**
* Called by egw::egw_final to store the title-cache in the session
*
@ -138,7 +138,7 @@ class egw_link extends solink
{
$GLOBALS['egw']->session->appsession('link_title_cache','phpgwapi',self::$title_cache);
}
/**
* creats a link between $app1,$id1 and $app2,$id2 - $id1 does NOT need to exist yet
*
@ -146,7 +146,7 @@ class egw_link extends solink
* File-attachments return a negative link-id !!!
*
* @param string $app1 app of $id1
* @param string/array &$id1 id of item to linkto or 0 if item not yet created or array with links
* @param string/array &$id1 id of item to linkto or 0 if item not yet created or array with links
* of not created item or $file-array if $app1 == self::VFS_APPNAME (see below).
* If $id==0 it will be set on return to an array with the links for the new item.
* @param string/array $app2 app of 2.linkend or array with links ($id2 not used)
@ -214,7 +214,7 @@ class egw_link extends solink
{
$link_id = solink::link($app1,$id1,$link['app'],$link['id'],
$link['remark'],$link['owner'],$link['lastmod']);
// notify both sides
if (!($no_notify&2)) self::notify('link',$link['app'],$link['id'],$app1,$id1,$link_id);
if (!($no_notify&1)) self::notify('link',$app1,$id1,$link['app'],$link['id'],$link_id);
@ -234,7 +234,7 @@ class egw_link extends solink
if (!($no_notify&2)) self::notify('link',$app2,$id2,$app1,$id1,$link_id);
if (!($no_notify&1)) self::notify('link',$app1,$id1,$app2,$id2,$link_id);
return $link_id;
}
@ -272,7 +272,7 @@ class egw_link extends solink
{
$only_app = substr(1,$only_app);
}
foreach (array_reverse($id) as $link)
foreach (array_reverse($id) as $link)
{
if (is_array($link) // check for unlink-marker
&& !($only_app && $not_only == ($link['app'] == $only_app)))
@ -296,7 +296,7 @@ class egw_link extends solink
return $ids;
}
/**
* Query the links of multiple entries of one application
*
@ -363,17 +363,17 @@ class egw_link extends solink
* @param string $app2='' second app
* @param string $id2='' id in $app2
* @return array with link-data or False
*/
*/
static function get_link($app_link_id,$id='',$app2='',$id2='')
{
if (self::DEBUG)
{
echo '<p>'.__METHOD__."($app_link_id,$id,$app2,$id2)</p>\n"; echo function_backtrace();
echo '<p>'.__METHOD__."($app_link_id,$id,$app2,$id2)</p>\n"; echo function_backtrace();
}
if (is_array($id))
{
if (strpos($app_link_id,':') === false) $app_link_id = self::temp_link_id($app2,$id2); // create link_id of temporary link, if not given
if (isset($id[$app_link_id]) && is_array($id[$app_link_id])) // check for unlinked-marker
{
return $id[$app_link_id];
@ -449,7 +449,7 @@ class egw_link extends solink
unset(self::$title_cache[$app.':'.$id]);
}
$deleted =& solink::unlink($link_id,$app,$id,$owner,$app2 != '!'.self::VFS_APPNAME ? $app2 : '',$id2);
// only notify on real links, not the one cached for writing or fileattachments
self::notify_unlink($deleted);
@ -517,14 +517,14 @@ class egw_link extends solink
* returns the title (short description) of entry $id and $app
*
* @param string $app appname
* @param string $id id in $app
* @param string $id id in $app
* @param array $link=null link-data for file-attachments
* @return string/boolean string with title, null if $id does not exist in $app or false if no perms to view it
*/
static function title($app,$id,$link=null)
{
if (!$id) return '';
if (isset(self::$title_cache[$app.':'.$id]))
{
if (self::DEBUG) echo '<p>'.__METHOD__."('$app','$id')='".self::$title_cache[$app.':'.$id]."' (from cache)</p>\n";
@ -562,10 +562,10 @@ class egw_link extends solink
return self::$title_cache[$app.':'.$id] = $title;
}
/**
* Query the titles off multiple id's of one app
*
*
* Apps can implement that hook, if they have a quicker (eg. less DB queries) method to query the title of multiple entries.
* If it's not implemented, we call the regular title method multiple times.
*
@ -609,7 +609,7 @@ class egw_link extends solink
*
* @param string $app appname of entry to create
* @param string $to_app appname to link the new entry to
* @param string $to_id id in $to_app
* @param string $to_id id in $to_app
* @return array/boolean with name-value pairs for link to add-methode of $app or false if add not supported
*/
static function add($app,$to_app='',$to_id='')
@ -620,7 +620,7 @@ class egw_link extends solink
return false;
}
$params = $reg['add'];
if ($reg['add_app'] && $to_app && $reg['add_id'] && $to_id)
{
$params[$reg['add_app']] = $to_app;
@ -633,7 +633,7 @@ class egw_link extends solink
* view entry $id of $app
*
* @param string $app appname
* @param string $id id in $app
* @param string $id id in $app
* @param array $link=null link-data for file-attachments
* @return array with name-value pairs for link to view-methode of $app to view $id
*/
@ -675,7 +675,7 @@ class egw_link extends solink
static function is_popup($app,$action='view')
{
return self::get_registry($app,$action.'_popup');
}
}
/**
* Check if $app is in the registry and has an entry for $name
@ -689,7 +689,7 @@ class egw_link extends solink
$reg = self::$app_register[$app];
return isset($reg) ? $reg[$name] : false;
}
}
/**
* path to the attached files of $app/$ip or the directory for $app if no $id,$file given
@ -698,7 +698,7 @@ class egw_link extends solink
* separate subdirs with name app are created.
*
* @param string $app appname
* @param string $id='' id in $app
* @param string $id='' id in $app
* @param string $file='' filename
* @return string/array path or array with path and relatives, depending on $relatives
*/
@ -708,11 +708,11 @@ class egw_link extends solink
if ($app)
{
$path .= '/'.$app;
if ($id)
{
$path .= '/'.$id;
if ($file)
{
$path .= '/'.$file;
@ -727,7 +727,7 @@ class egw_link extends solink
* Put a file to the corrosponding place in the VFS and set the attributes
*
* @param string $app appname to linke the file to
* @param string $id id in $app
* @param string $id id in $app
* @param array $file informations about the file in format of the etemplate file-type
* $file['name'] name of the file (no directory)
* $file['type'] mine-type of the file
@ -744,34 +744,18 @@ class egw_link extends solink
{
echo "<p>attach_file: app='$app', id='$id', tmp_name='$file[tmp_name]', name='$file[name]', size='$file[size]', type='$file[type]', path='$file[path]', ip='$file[ip]', comment='$comment'</p>\n";
}
$app_dir = self::vfs_path($app);
// we dont want an owner, as this would give rights independent of the apps ACL
$current_user = egw_vfs::$user; egw_vfs::$user = 0;
$Ok = true;
if (!file_exists($app_dir))
{
egw_vfs::$is_root = true;
$Ok = mkdir($app_dir,0700,true);
if (!$Ok) echo "<p>Can't mkdir($app_dir,0700,true)!</p>\n";
egw_vfs::$is_root = false;
}
$entry_dir = self::vfs_path($app,$id);
if ($Ok && !file_exists($entry_dir))
{
$Ok = mkdir($entry_dir,0700);
}
egw_vfs::$user = $current_user;
if ($Ok)
if (file_exists($entry_dir) || ($Ok = mkdir($entry_dir,0,true)))
{
$Ok = copy($file['tmp_name'],$fname = $entry_dir.'/'.$file['name']) &&
($stat = links_stream_wrapper::url_stat($fname,0));
}
}
else
{
error_log(__METHOD__."($app,$id,$file,$comment) Can't mkdir $entry_dir!");
}
// todo: set comment
return $Ok ? -$stat['ino'] : false;
}
@ -781,6 +765,7 @@ class egw_link extends solink
* @param int/string $app > 0: file_id of an attchemnt or $app/$id entry which linked to
* @param string $id='' id in app
* @param string $fname filename
* @return boolean/array false on error ($app or $id not found), array with path as key and boolean result of delete
*/
static function delete_attached($app,$id='',$fname = '')
{
@ -912,7 +897,7 @@ class egw_link extends solink
*
* Please note: not all apps supply update notifications
*
* @internal
* @internal
* @param string $type 'link' for new links, 'unlink' for unlinked entries, 'update' of content in linked entries
* @param string $notify_app app to notify
* @param string $notify_id id in $notify_app
@ -938,7 +923,7 @@ class egw_link extends solink
/**
* notifies about unlinked links
*
* @internal
* @internal
* @param array &$links unlinked links from the database
*/
static private function notify_unlink(&$links)
@ -948,7 +933,7 @@ class egw_link extends solink
// we notify both sides of the link, as the unlink command NOT clearly knows which side initiated the unlink
self::notify('unlink',$link['link_app1'],$link['link_id1'],$link['link_app2'],$link['link_id2'],$link['link_id']);
self::notify('unlink',$link['link_app2'],$link['link_id2'],$link['link_app1'],$link['link_id1'],$link['link_id']);
}
}
}
}
egw_link::init_static();

View File

@ -13,21 +13,21 @@
/**
* eGroupWare API: stream wrapper for linked files
*
*
* The files stored by the sqlfs_stream_wrapper in a /apps/$app/$id directory
*
*
* The links stream wrapper extends the sqlfs one, to implement an own ACL based on the access
* of the entry the files are linked to.
*
*
* Applications can define a 'file_access' method in the link registry with the following signature:
*
*
* boolean function file_access(string $id,int $check,string $rel_path)
*
*
* If the do not implement such a function the title function is used to test if the user has
* at least read access to an entry, and if true full (write) access to the files is granted.
*
*
* The stream wrapper interface is according to the docu on php.net
*
*
* @link http://de.php.net/manual/de/function.stream-wrapper-register.php
*/
class links_stream_wrapper extends sqlfs_stream_wrapper
@ -51,7 +51,7 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
/**
* Implements ACL based on the access of the user to the entry the files are linked to.
*
*
* @param string $url url to check
* @param int $check mode to check: one or more or'ed together of: 4 = read, 2 = write, 1 = executable
* @return boolean
@ -59,7 +59,7 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
static function check_extended_acl($url,$check)
{
$path = parse_url($url,PHP_URL_PATH);
list(,$apps,$app,$id,$rel_path) = explode('/',$path,5);
if ($apps != 'apps')
@ -95,19 +95,19 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
/**
* This method is called in response to stat() calls on the URL paths associated with the wrapper.
*
*
* Reimplemented from sqlfs, as we have to pass the value of check_extends_acl(), due to the lack of late static binding.
*
*
* @param string $path
* @param int $flags holds additional flags set by the streams API. It can hold one or more of the following values OR'd together:
* - STREAM_URL_STAT_LINK For resources with the ability to link to other resource (such as an HTTP Location: forward,
* or a filesystem symlink). This flag specified that only information about the link itself should be returned,
* not the resource pointed to by the link.
* - STREAM_URL_STAT_LINK For resources with the ability to link to other resource (such as an HTTP Location: forward,
* or a filesystem symlink). This flag specified that only information about the link itself should be returned,
* not the resource pointed to by the link.
* This flag is set in response to calls to lstat(), is_link(), or filetype().
* - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set,
* - STREAM_URL_STAT_QUIET If this flag is set, your wrapper should not raise any errors. If this flag is not set,
* you are responsible for reporting errors using the trigger_error() function during stating of the path.
* stat triggers it's own warning anyway, so it makes no sense to trigger one by our stream-wrapper!
* @return array
* @return array
*/
static function url_stat ( $url, $flags )
{
@ -116,7 +116,7 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
/**
* Set or delete extended acl for a given path and owner (or delete them if is_null($rights)
*
*
* Reimplemented, to NOT call the sqlfs functions, as we dont allow to modify the ACL (defined by the apps)
*
* @param string $path string with path
@ -129,10 +129,10 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
{
return false;
}
/**
* Get all ext. ACL set for a path
*
*
* Reimplemented, to NOT call the sqlfs functions, as we dont allow to modify the ACL (defined by the apps)
*
* @param string $path
@ -142,6 +142,44 @@ class links_stream_wrapper extends sqlfs_stream_wrapper
{
return false;
}
/**
* mkdir for links
*
* 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 dirs
*
* This method is called in response to mkdir() calls on URL paths associated with the wrapper.
*
* It should attempt to create the directory specified by path.
* In order for the appropriate error message to be returned, do not define this method if your wrapper does not support creating directories.
*
* @param string $path
* @param int $mode not used(!), we inherit 005 for /apps/$app and set 000 for /apps/$app/$id
* @param int $options Posible values include STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE, we allways use recursive!
* @return boolean TRUE on success or FALSE on failure
*/
static function mkdir($path,$mode,$options)
{
if($path[0] != '/') $path = parse_url($path,PHP_URL_PATH);
list(,$apps,$app,$id,$rel_path) = explode('/',$path,5);
$ret = false;
if ($apps == 'apps' && $app && !$id || self::check_extended_acl($path,egw_vfs::WRITABLE)) // app directory itself is allways ok
{
egw_vfs::$is_root = true;
$current_user = egw_vfs::$user; egw_vfs::$user = 0;
$ret = parent::mkdir($path,0,$options|STREAM_MKDIR_RECURSIVE);
if ($id) parent::chmod($path,0); // no other rights
egw_vfs::$user = $current_user;
egw_vfs::$is_root = false;
}
//error_log(__METHOD__."($path,$mode,$options) apps=$apps, app=$app, id=$id: returning $ret");
return $ret;
}
}
stream_register_wrapper(links_stream_wrapper::SCHEME ,'links_stream_wrapper');