From 0080dc39f0e8588d2a5f67409de47276a2ca2edb Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 21 Apr 2008 11:06:45 +0000 Subject: [PATCH] special mkdir of links_stream_wrapper, so the entry dirs do not inherit the other read/browse rights from the app dir --- phpgwapi/inc/class.egw_link.inc.php | 101 ++++++++---------- .../inc/class.links_stream_wrapper.inc.php | 76 +++++++++---- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/phpgwapi/inc/class.egw_link.inc.php b/phpgwapi/inc/class.egw_link.inc.php index 071e82a2da..1858236a4e 100644 --- a/phpgwapi/inc/class.egw_link.inc.php +++ b/phpgwapi/inc/class.egw_link.inc.php @@ -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 '

'.__METHOD__."($app_link_id,$id,$app2,$id2)

\n"; echo function_backtrace(); + echo '

'.__METHOD__."($app_link_id,$id,$app2,$id2)

\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 '

'.__METHOD__."('$app','$id')='".self::$title_cache[$app.':'.$id]."' (from cache)

\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 "

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'

\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 "

Can't mkdir($app_dir,0700,true)!

\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(); \ No newline at end of file diff --git a/phpgwapi/inc/class.links_stream_wrapper.inc.php b/phpgwapi/inc/class.links_stream_wrapper.inc.php index ffb315883e..5d28c92525 100644 --- a/phpgwapi/inc/class.links_stream_wrapper.inc.php +++ b/phpgwapi/inc/class.links_stream_wrapper.inc.php @@ -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');