From 5eeac7093cd01b47fc59ec4c85dc4f1f86609eff Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 18 Nov 2014 12:55:32 +0000 Subject: [PATCH] use filemanager UI as UI for sharing directories --- etemplate/inc/class.etemplate_new.inc.php | 7 +- filemanager/inc/class.filemanager_ui.inc.php | 95 +++++++------ phpgwapi/inc/class.egw.inc.php | 24 ++-- phpgwapi/inc/class.egw_digest_auth.inc.php | 12 +- phpgwapi/inc/class.egw_framework.inc.php | 4 +- phpgwapi/inc/class.egw_session.inc.php | 111 +++++++-------- phpgwapi/inc/class.egw_sharing.inc.php | 138 ++++++++++++++++--- phpgwapi/inc/class.egw_vfs.inc.php | 4 +- phpgwapi/js/jsapi/egw_links.js | 2 +- phpgwapi/lang.php | 2 +- share.php | 20 ++- 11 files changed, 273 insertions(+), 146 deletions(-) diff --git a/etemplate/inc/class.etemplate_new.inc.php b/etemplate/inc/class.etemplate_new.inc.php index 4ecb28b0aa..4eb5ddf6d7 100644 --- a/etemplate/inc/class.etemplate_new.inc.php +++ b/etemplate/inc/class.etemplate_new.inc.php @@ -206,9 +206,12 @@ class etemplate_new extends etemplate_widget_template return; } // let framework know, if we are a popup or not ('popup' not true, which is allways used by index.php!) - $GLOBALS['egw_info']['flags']['nonavbar'] = $output_mode == 2 ? 'popup' : false; + if (!isset($GLOBALS['egw_info']['flags']['nonavbar']) || is_bool($GLOBALS['egw_info']['flags']['nonavbar'])) + { + $GLOBALS['egw_info']['flags']['nonavbar'] = $output_mode == 2 ? 'popup' : false; + } echo $GLOBALS['egw']->framework->header(); - if ($output_mode != 2) + if ($output_mode != 2 && !$GLOBALS['egw_info']['flags']['nonavbar']) { parse_navbar(); } diff --git a/filemanager/inc/class.filemanager_ui.inc.php b/filemanager/inc/class.filemanager_ui.inc.php index dd9f0623c8..e4cf32f9c8 100644 --- a/filemanager/inc/class.filemanager_ui.inc.php +++ b/filemanager/inc/class.filemanager_ui.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @package filemanager * @author Ralf Becker - * @copyright (c) 2008-13 by Ralf Becker + * @copyright (c) 2008-14 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -58,8 +58,8 @@ class filemanager_ui egw_vfs::$is_root = true; } - self::init_views(); - self::$merge_prop_namespace = egw_vfs::DEFAULT_PROP_NAMESPACE.$GLOBALS['egw_info']['flags']['currentapp']; + static::init_views(); + static::$merge_prop_namespace = egw_vfs::DEFAULT_PROP_NAMESPACE.$GLOBALS['egw_info']['flags']['currentapp']; } /** @@ -69,21 +69,21 @@ class filemanager_ui */ public static function init_views() { - if (!self::$views_init) + if (!static::$views_init) { // translate our labels - foreach(self::$views as $method => &$label) + foreach(static::$views as &$label) { $label = lang($label); } // search for plugins with additional filemanager views - foreach($GLOBALS['egw']->hooks->process('filemanager_views') as $app => $views) + foreach($GLOBALS['egw']->hooks->process('filemanager_views') as $views) { - if (is_array($views)) self::$views += $views; + if (is_array($views)) static::$views += $views; } - self::$views_init = true; + static::$views_init = true; } - return self::$views; + return static::$views; } /** @@ -98,10 +98,10 @@ class filemanager_ui { $view = $_GET['view']; } - if (!isset(self::$views[$view])) + if (!isset(static::$views[$view])) { - reset(self::$views); - $view = key(self::$views); + reset(static::$views); + $view = key(static::$views); } return $view; } @@ -224,7 +224,7 @@ class filemanager_ui switch($scope) { case 'self': - $props = egw_vfs::propfind($path, self::$merge_prop_namespace); + $props = egw_vfs::propfind($path, static::$merge_prop_namespace); $app = empty($props) ? null : $props[0]['val']; break; case 'parents': @@ -232,7 +232,7 @@ class filemanager_ui $currentpath = $path; while($dir = egw_vfs::dirname($currentpath)) { - $props = egw_vfs::propfind($dir, self::$merge_prop_namespace); + $props = egw_vfs::propfind($dir, static::$merge_prop_namespace); if(!empty($props)) { // found prop in parent directory @@ -274,7 +274,7 @@ class filemanager_ui 'default_cols' => '!comment,ctime', // I columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns 'csv_fields' => false, // I false=disable csv export, true or unset=enable it with auto-detected fieldnames, //or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type) - 'actions' => self::get_actions(), + 'actions' => static::get_actions(), 'row_id' => 'path', 'row_modified' => 'mtime', 'parent_id' => 'dir', @@ -284,9 +284,9 @@ class filemanager_ui 'favorites' => true, 'placeholder_actions' => array('file_drop_mail','file_drop_move','file_drop_copy','file_drop_symlink') ); - $content['nm']['path'] = self::get_home_dir(); + $content['nm']['path'] = static::get_home_dir(); } - $content['nm']['home_dir'] = self::get_home_dir(); + $content['nm']['home_dir'] = static::get_home_dir(); if (isset($_GET['msg'])) $msg = $_GET['msg']; @@ -303,7 +303,7 @@ class filemanager_ui $path = egw_vfs::dirname($content['nm']['path']); break; case '~': - $path = self::get_home_dir(); + $path = static::get_home_dir(); break; } if ($path[0] == '/' && egw_vfs::stat($path,true) && egw_vfs::is_dir($path) && egw_vfs::check_access($path,egw_vfs::READABLE)) @@ -320,7 +320,7 @@ class filemanager_ui if (!$content['nm']['filter']) $content['nm']['filter'] = ''; } } - $view = self::get_view(); + $view = static::get_view(); if (strpos($view,'::') !== false && version_compare(PHP_VERSION,'5.3.0','<')) { @@ -375,7 +375,7 @@ class filemanager_ui { if ($content['nm']['action']) { - $msg = self::action($content['nm']['action'],$content['nm']['selected'],$content['nm']['path']); + $msg = static::action($content['nm']['action'],$content['nm']['selected'],$content['nm']['path']); if($msg) egw_framework::message($msg); // clean up after action @@ -389,7 +389,7 @@ class filemanager_ui } elseif($content['nm']['rows']['delete']) { - $msg = self::action('delete',array_keys($content['nm']['rows']['delete']),$content['nm']['path']); + $msg = static::action('delete',array_keys($content['nm']['rows']['delete']),$content['nm']['path']); if($msg) egw_framework::message($msg); // clean up after action @@ -483,6 +483,10 @@ class filemanager_ui { $start = $path; } + elseif (!egw_vfs::is_dir($start) && egw_vfs::check_access($start, egw_vfs::READABLE)) + { + $start = '/'; + } return $start; } @@ -511,7 +515,7 @@ class filemanager_ui throw new egw_exception_assertion_failed('Implemented on clientside!'); case 'delete': - return self::do_delete($selected,$errs,$files,$dirs); + return static::do_delete($selected,$errs,$files,$dirs); case 'copy': foreach($selected as $path) @@ -650,7 +654,7 @@ class filemanager_ui $document_merge = new filemanager_merge(egw_vfs::decodePath($dir)); $msg = $document_merge->download($settings, $selected, '', $GLOBALS['egw_info']['user']['preferences']['filemanager']['document_dir']); if($msg) return $msg; - $failed = count($selected); + $errs = count($selected); return false; } } @@ -732,9 +736,8 @@ class filemanager_ui * * @param array $query * @param array &$rows - * @param array &$readonlys */ - function get_rows($query,&$rows,&$readonlys) + function get_rows($query, &$rows) { // show projectmanager sidebox for projectmanager path if (substr($query['path'],0,20) == '/apps/projectmanager' && isset($GLOBALS['egw_info']['user']['apps']['projectmanager'])) @@ -746,7 +749,7 @@ class filemanager_ui { egw_session::appsession('index','filemanager',$query); } - if(!$query['path']) $query['path'] = self::get_home_dir(); + if(!$query['path']) $query['path'] = static::get_home_dir(); // be tolerant with (in previous versions) not correct urlencoded pathes if (!egw_vfs::stat($query['path'],true) && egw_vfs::stat(urldecode($query['path']))) @@ -755,13 +758,19 @@ class filemanager_ui } if (!egw_vfs::stat($query['path'],true) || !egw_vfs::is_dir($query['path']) || !egw_vfs::check_access($query['path'],egw_vfs::READABLE)) { - // we will leave here, since we are not allowed, or the location does not exist. Index must handle that, and give - // 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.',egw_vfs::decodePath($query['path'])), - 'ajax' => 'true' - )); + // only redirect, if it would be to some other location, gives redirect-loop otherwise + if ($query['path'] != ($path = static::get_home_dir())) + { + // we will leave here, since we are not allowed, or the location does not exist. Index must handle that, and give + // an appropriate message + egw::redirect_link('/index.php',array('menuaction'=>'filemanager.filemanager_ui.index', + 'path' => $path, + 'msg' => lang('The requested path %1 is not available.',egw_vfs::decodePath($query['path'])), + 'ajax' => 'true' + )); + } + $rows = array(); + return 0; } $rows = $dir_is_writable = array(); if($query['searchletter'] && !empty($query['search'])) @@ -787,6 +796,7 @@ class filemanager_ui $maxdepth = $filter && $filter != 4 ? (int)(boolean)$filter : null; if($filter == 5) $maxdepth = 2; + $n = 0; foreach(egw_vfs::find(!empty($query['col_filter']['dir']) ? $query['col_filter']['dir'] : $query['path'],array( 'mindepth' => 1, 'maxdepth' => $maxdepth, @@ -946,6 +956,7 @@ class filemanager_ui if (in_array($button,array('save','apply'))) { $props = array(); + $perm_changed = $perm_failed = 0; foreach($content['old'] as $name => $old_value) { if (isset($content[$name]) && ($old_value != $content[$name] || @@ -989,7 +1000,7 @@ class filemanager_ui { $mergeprop = array( array( - 'ns' => self::$merge_prop_namespace, + 'ns' => static::$merge_prop_namespace, 'name' => 'mergeapp', 'val' => (!empty($content[$name]) ? $content[$name] : null), ), @@ -1008,7 +1019,7 @@ class filemanager_ui { static $name2cmd = array('uid' => 'chown','gid' => 'chgrp','perms' => 'chmod'); $cmd = array('egw_vfs',$name2cmd[$name]); - $value = $name == 'perms' ? self::perms2mode($content['perms']) : $content[$name]; + $value = $name == 'perms' ? static::perms2mode($content['perms']) : $content[$name]; if ($content['modify_subs']) { if ($name == 'perms') @@ -1076,7 +1087,7 @@ class filemanager_ui if ($content['eacl']['delete']) { list($ino_owner) = each($content['eacl']['delete']); - list($ino,$owner) = explode('-',$ino_owner,2); // $owner is a group and starts with a minus! + list(, $owner) = explode('-',$ino_owner,2); // $owner is a group and starts with a minus! $msg .= egw_vfs::eacl($path,null,$owner) ? lang('ACL deleted.') : lang('Error deleting the ACL entry!'); } elseif ($button == 'eacl') @@ -1165,8 +1176,8 @@ class filemanager_ui } // mergeapp - $content['mergeapp'] = self::get_mergeapp($path, 'self'); - $content['mergeapp_parent'] = self::get_mergeapp($path, 'parents'); + $content['mergeapp'] = static::get_mergeapp($path, 'self'); + $content['mergeapp_parent'] = static::get_mergeapp($path, 'parents'); if(!empty($content['mergeapp'])) { $content['mergeapp_effective'] = $content['mergeapp']; @@ -1225,7 +1236,7 @@ class filemanager_ui $tpl->setElementAttribute('tabs', 'add_tabs', true); $tabs =& $tpl->getElementAttribute('tabs','tabs'); - $tabs = array(); + if (true) $tabs = array(); foreach(isset($extra_tabs[0]) ? $extra_tabs : array($extra_tabs) as $extra_tab) { @@ -1259,7 +1270,7 @@ class filemanager_ui * @param string $action eg. 'delete', ... * @param array $selected selected path(s) * @param string $dir=null current directory - * @see self::action() + * @see static::action() */ public static function ajax_action($action, $selected, $dir=null, $props=null) { @@ -1344,7 +1355,7 @@ class filemanager_ui // Upload, then link case 'link': // First upload - $arr = self::ajax_action('upload', $selected, $dir, $props); + $arr = static::ajax_action('upload', $selected, $dir, $props); $app_dir = egw_link::vfs_path($props['entry']['app'],$props['entry']['id'],'',true); foreach($arr['uploaded'] as $file) @@ -1361,7 +1372,7 @@ class filemanager_ui return; default: - $arr['msg'] = self::action($action, $selected, $dir, $arr['errs'], $arr['dirs'], $arr['files']); + $arr['msg'] = static::action($action, $selected, $dir, $arr['errs'], $arr['dirs'], $arr['files']); } $response->data($arr); //error_log(__METHOD__."('$action',".array2string($selected).') returning '.array2string($arr)); diff --git a/phpgwapi/inc/class.egw.inc.php b/phpgwapi/inc/class.egw.inc.php index fbf00bfd8c..91ec2000f2 100644 --- a/phpgwapi/inc/class.egw.inc.php +++ b/phpgwapi/inc/class.egw.inc.php @@ -344,6 +344,10 @@ class egw extends egw_minimal * Verify the user has rights for the requested app * * If the user has no rights for the app (eg. called via URL) he get a permission denied page (this function does NOT return) + * + * @throws egw_exception_redirect for anonymous user accessing something he has no rights to + * @throws egw_exception_no_permission_admin + * @throws egw_exception_no_permission_app */ function check_app_rights() { @@ -358,7 +362,7 @@ class egw extends egw_minimal // present a login page, if anon user has no right for an application if ($this->session->session_flags == 'A') { - egw::redirect_link('/logout.php'); + throw new egw_exception_redirect('/logout.php'); } if ($currentapp == 'admin' || $GLOBALS['egw_info']['flags']['admin_only']) { @@ -440,9 +444,9 @@ class egw extends egw_minimal /** * Link url generator * - * @param string $string The url the link is for - * @param string|array $extravars='' Extra params to be passed to the url - * @param string $link_app=null if appname or true, some templates generate a special link-handler url + * @param string $url url link is for + * @param string|array $extravars ='' extra params to be added to url + * @param string $link_app =null if appname or true, some templates generate a special link-handler url * @return string The full url after processing */ static function link($url, $extravars = '', $link_app=null) @@ -453,9 +457,9 @@ class egw extends egw_minimal /** * Redirects direct to a generated link * - * @param string $string The url the link is for - * @param string/array $extravars Extra params to be passed to the url - * @param string $link_app=null if appname or true, some templates generate a special link-handler url + * @param string $url url link is for + * @param string|array $extravars ='' extra params to be added to url + * @param string $link_app =null if appname or true, some templates generate a special link-handler url * @return string The full url after processing */ static function redirect_link($url, $extravars='', $link_app=null) @@ -468,8 +472,8 @@ class egw extends egw_minimal * * This function handles redirects under iis and apache it assumes that $phpgw->link() has already been called * - * @param string The url ro redirect to - * @param string $link_app=null appname to redirect for, default currentapp + * @param string $url url to redirect to + * @param string $link_app =null appname to redirect for, default currentapp */ static function redirect($url, $link_app=null) { @@ -547,7 +551,7 @@ class egw extends egw_minimal * garanties to run AFTER output send to user. * * @param callable $callback use array($classname, $method) for static methods - * @param array $args=array() + * @param array $args =array() */ public static function on_shutdown($callback, array $args=array()) { diff --git a/phpgwapi/inc/class.egw_digest_auth.inc.php b/phpgwapi/inc/class.egw_digest_auth.inc.php index 193cc71427..6ff8b20eb0 100644 --- a/phpgwapi/inc/class.egw_digest_auth.inc.php +++ b/phpgwapi/inc/class.egw_digest_auth.inc.php @@ -71,6 +71,7 @@ class egw_digest_auth */ static public function autocreate_session_callback(&$account) { + unset($account); // not used, but required by function signature if (self::ERROR_LOG) { $pw = self::ERROR_LOG > 1 ? $_SERVER['PHP_AUTH_PW'] : '**********'; @@ -109,7 +110,8 @@ class egw_digest_auth $password = translation::convert($password, 'iso-8859-1'); //error_log(__METHOD__."() Fixed non-ascii password of user '$username' from '$_SERVER[PHP_AUTH_PW]' to '$password'"); } - if (!isset($username) || !($sessionid = $GLOBALS['egw']->session->create($username, $password, 'text'))) + // create session without session cookie (session->create(..., true), as we use pseudo sessionid from credentials + if (!isset($username) || !($sessionid = $GLOBALS['egw']->session->create($username, $password, 'text', true))) { // if the session class gives a reason why the login failed --> append it to the REALM if ($GLOBALS['egw']->session->reason) $realm .= ': '.$GLOBALS['egw']->session->reason; @@ -130,8 +132,8 @@ class egw_digest_auth * If no user is given, check is NOT authoretive, as we can only check if cleartext passwords are generally used * * @param string $realm - * @param string $username=null username or null to only check if we auth agains sql and use plaintext passwords - * @param string &$user_pw=null stored cleartext password, if $username given AND function returns true + * @param string $username =null username or null to only check if we auth agains sql and use plaintext passwords + * @param string &$user_pw =null stored cleartext password, if $username given AND function returns true * @return boolean true if digest auth is available, false otherwise */ static public function digest_auth_available($realm,$username=null,&$user_pw=null) @@ -181,7 +183,7 @@ class egw_digest_auth * Check digest * * @param string $realm - * @param string $auth_digest=null default to $_SERVER['PHP_AUTH_DIGEST'] + * @param string $auth_digest =null default to $_SERVER['PHP_AUTH_DIGEST'] * @param string &$username on return username * @param string &$password on return cleartext password * @return boolean true if digest is correct, false otherwise @@ -215,6 +217,7 @@ class egw_digest_auth */ static private function get_digest_A1($realm,$username,&$password=null) { + $user_pw = null; if (empty($username) || empty($realm) || !self::digest_auth_available($realm,$username,$user_pw)) { return false; @@ -236,6 +239,7 @@ class egw_digest_auth $data = array(); $keys = implode('|', array_keys($needed_parts)); + $matches = null; preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); foreach ($matches as $m) diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php index 7e0560ca08..11cc49841f 100644 --- a/phpgwapi/inc/class.egw_framework.inc.php +++ b/phpgwapi/inc/class.egw_framework.inc.php @@ -2068,9 +2068,7 @@ abstract class egw_framework // generate jdots bundle, if installed if (file_exists(EGW_SERVER_ROOT.'/jdots')) { - $inc_mgr->include_js_file('/jdots/js/egw_fw.js'); - $inc_mgr->include_js_file('/jdots/js/egw_fw_ui.js'); - $inc_mgr->include_js_file('/jdots/js/egw_fw_classes.js'); + $inc_mgr->include_js_file('/jdots/js/fw_jdots.js'); $bundles['jdots'] = array_diff($inc_mgr->get_included_files(), call_user_func_array('array_merge', $bundles)); self::bundle_urls($bundles['jdots'], $jdots_max_mod); } diff --git a/phpgwapi/inc/class.egw_session.inc.php b/phpgwapi/inc/class.egw_session.inc.php index dce6d6818b..00968c2aa0 100644 --- a/phpgwapi/inc/class.egw_session.inc.php +++ b/phpgwapi/inc/class.egw_session.inc.php @@ -169,7 +169,7 @@ class egw_session /** * Constructor just loads up some defaults from cookies * - * @param array $domain_names=null domain-names used in this install + * @param array $domain_names =null domain-names used in this install */ function __construct(array $domain_names=null) { @@ -323,8 +323,8 @@ class egw_session * * @param array &$arr * @param string $label - * @param boolean $recursion=true if true call itself for every item > $limit - * @param int $limit=1000 log only differences > $limit + * @param boolean $recursion =true if true call itself for every item > $limit + * @param int $limit =1000 log only differences > $limit */ static function log_session_usage(&$arr,$label,$recursion=true,$limit=1000) { @@ -375,8 +375,8 @@ class egw_session * * @param string $kp3 mcrypt key transported via cookie or get parameter like the session id, * unlike the session id it's not know on the server, so only the client-request can decrypt the session! - * @param string $algo=self::MCRYPT_ALGO - * @param string $mode=self::MCRYPT_MODE + * @param string $algo =self::MCRYPT_ALGO + * @param string $mode =self::MCRYPT_MODE * @return boolean true if encryption is used, false otherwise */ static private function init_crypt($kp3,$algo=self::MCRYPT_ALGO,$mode=self::MCRYPT_MODE) @@ -420,8 +420,8 @@ class egw_session * @param string $login user login * @param string $passwd user password * @param string $passwd_type type of password being used, ie plaintext, md5, sha1 - * @param boolean $no_session_needed=false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support - * @param boolean $auth_check=true if false, the user is loged in without checking his password (eg. for single sign on), default = true + * @param boolean $no_session =false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support + * @param boolean $auth_check =true if false, the user is loged in without checking his password (eg. for single sign on), default = true * @return string session id */ function create($login,$passwd = '',$passwd_type = '',$no_session=false,$auth_check=true) @@ -520,11 +520,10 @@ class egw_session $GLOBALS['egw_info']['user']['account_id'] = $this->account_id; $GLOBALS['egw']->accounts->accounts($this->account_id); - // for WebDAV and GroupDAV we use a pseudo sessionid created from md5(user:passwd) + // for *DAV and eSync we use a pseudo sessionid created from md5(user:passwd) // --> allows this stateless protocolls which use basic auth to use sessions! if (($this->sessionid = self::get_sessionid(true))) { - $no_session = true; // no need to set cookie session_id($this->sessionid); } else @@ -572,7 +571,7 @@ class egw_session 'user_ip' => $user_ip, ),'',true))) // true = run hooks from all apps, not just the ones the current user has perms to run { - foreach($hook_result as $app => $reason) + foreach($hook_result as $reason) { if ($reason) // called hook requests to deny the session { @@ -649,9 +648,9 @@ class egw_session * Write or update (for logout) the access_log * * @param string|int $sessionid nummeric or PHP session id or 0 for unsuccessful logins - * @param string $login='' account_lid (evtl. with domain) or '' for setting the logout-time - * @param string $user_ip='' ip to log - * @param int $account_id=0 numerical account_id + * @param string $login ='' account_lid (evtl. with domain) or '' for setting the logout-time + * @param string $user_ip ='' ip to log + * @param int $account_id =0 numerical account_id * @return int $sessionid primary key of egw_access_log for login, null otherwise */ private function log_access($sessionid,$login='',$user_ip='',$account_id=0) @@ -708,7 +707,6 @@ class egw_session */ private function login_blocked($login,$ip) { - $blocked = false; $block_time = time() - $GLOBALS['egw_info']['server']['block_time'] * 60; $false_id = $false_ip = 0; @@ -764,7 +762,7 @@ class egw_session } catch(Exception $e) { // ignore exception, but log it, to block the account and give a correct error-message to user - error_log(__METHOD__."('$log', '$ip') ".$e->getMessage()); + error_log(__METHOD__."('$login', '$ip') ".$e->getMessage()); } } // save time of mail, to not send to many mails @@ -776,10 +774,19 @@ class egw_session return $blocked; } + /** + * Basename of scripts for which we create a pseudo session-id based on user-credentials + * + * @var array + */ + static $pseudo_session_scripts = array( + 'webdav.php', 'groupdav.php', 'remote.php', 'share.php' + ); + /** * Get the sessionid from Cookie, Get-Parameter or basic auth * - * @param boolean $only_basic_auth=false return only a basic auth pseudo sessionid, default no + * @param boolean $only_basic_auth =false return only a basic auth pseudo sessionid, default no * @return string */ static function get_sessionid($only_basic_auth=false) @@ -787,7 +794,7 @@ class egw_session // for WebDAV and GroupDAV we use a pseudo sessionid created from md5(user:passwd) // --> allows this stateless protocolls which use basic auth to use sessions! if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && - (in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php','remote.php')) || + (in_array(basename($_SERVER['SCRIPT_NAME']), self::$pseudo_session_scripts) || $_SERVER['SCRIPT_NAME'] === '/Microsoft-Server-ActiveSync')) { // we generate a pseudo-sessionid from the basic auth credentials @@ -799,7 +806,7 @@ class egw_session } // same for digest auth elseif (isset($_SERVER['PHP_AUTH_DIGEST']) && - in_array(basename($_SERVER['SCRIPT_NAME']),array('webdav.php','groupdav.php','remote.php'))) + in_array(basename($_SERVER['SCRIPT_NAME']), self::$pseudo_session_scripts)) { // we generate a pseudo-sessionid from the digest username, realm and nounce // can't use full $_SERVER['PHP_AUTH_DIGEST'], as it changes (contains eg. the url) @@ -897,20 +904,6 @@ class egw_session if($GLOBALS['egw_info']['user']['domain'] && $this->account_domain != $GLOBALS['egw_info']['user']['domain']) { return false; // session not verified, domain changed - - throw new Exception("Wrong domain! '$this->account_domain' != '{$GLOBALS['egw_info']['user']['domain']}'"); -/* if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."('$sessionid','$kp3') account_domain='$this->account_domain' != '{$GLOBALS['egw_info']['user']['domain']}'=egw_info[user][domain]"); - $GLOBALS['egw']->ADOdb = null; - $GLOBALS['egw_info']['user']['domain'] = $this->account_domain; - // reset the db - $GLOBALS['egw_info']['server']['db_host'] = $GLOBALS['egw_domain'][$this->account_domain]['db_host']; - $GLOBALS['egw_info']['server']['db_port'] = $GLOBALS['egw_domain'][$this->account_domain]['db_port']; - $GLOBALS['egw_info']['server']['db_name'] = $GLOBALS['egw_domain'][$this->account_domain]['db_name']; - $GLOBALS['egw_info']['server']['db_user'] = $GLOBALS['egw_domain'][$this->account_domain]['db_user']; - $GLOBALS['egw_info']['server']['db_pass'] = $GLOBALS['egw_domain'][$this->account_domain]['db_pass']; - $GLOBALS['egw_info']['server']['db_type'] = $GLOBALS['egw_domain'][$this->account_domain]['db_type']; - $GLOBALS['egw']->setup('',false); -*/ } $GLOBALS['egw_info']['user']['kp3'] = $this->kp3; @@ -1022,7 +1015,7 @@ class egw_session } $this->log_access($sessionid); // log logout-time - if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($sessionid,$kp3) parent::destroy()=$ret"); + if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($sessionid,$kp3)"); if (is_numeric($sessionid)) // do we have a access-log-id --> get PHP session id { @@ -1127,7 +1120,7 @@ class egw_session } // check if the url already contains a query and ensure that vars is an array and all strings are in extravars - list($url,$othervars) = explode('?',$url,2); + list($ret_url,$othervars) = explode('?', $url, 2); if ($extravars && is_array($extravars)) { $vars += $extravars; @@ -1174,18 +1167,17 @@ class egw_session $query[] = $key.'='.urlencode($value); } } - $url .= '?' . implode('&',$query); + $ret_url .= '?' . implode('&',$query); } - //echo " = '$url'

\n"; - return $url; + return $ret_url; } /** * Stores or retrieve applications data in/form the eGW session * * @param string $location free lable to store the data - * @param string $appname='' default current application (egw_info[flags][currentapp]) - * @param mixed $data='##NOTHING##' if given, data to store, if not specified + * @param string $appname ='' default current application (egw_info[flags][currentapp]) + * @param mixed $data ='##NOTHING##' if given, data to store, if not specified * @deprecated use egw_cache::setSession($appname, $location, $data) or egw_cache::getSession($appname, $location) * @return mixed session data or false if no data stored for $appname/$location */ @@ -1255,9 +1247,9 @@ class egw_session * Set a cookie with eGW's cookie-domain and -path settings * * @param string $cookiename name of cookie to be set - * @param string $cookievalue='' value to be used, if unset cookie is cleared (optional) - * @param int $cookietime=0 when cookie should expire, 0 for session only (optional) - * @param string $cookiepath=null optional path (eg. '/') if the eGW install-dir should not be used + * @param string $cookievalue ='' value to be used, if unset cookie is cleared (optional) + * @param int $cookietime =0 when cookie should expire, 0 for session only (optional) + * @param string $cookiepath =null optional path (eg. '/') if the eGW install-dir should not be used */ public static function egw_setcookie($cookiename,$cookievalue='',$cookietime=0,$cookiepath=null) { @@ -1269,12 +1261,11 @@ class egw_session if(!headers_sent()) // gives only a warning, but can not send the cookie anyway { - $rv = setcookie($cookiename,$cookievalue,$cookietime, + setcookie($cookiename,$cookievalue,$cookietime, is_null($cookiepath) ? self::$cookie_path : $cookiepath,self::$cookie_domain, // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) empty($GLOBALS['egw_info']['server']['insecure_cookies']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', true); } - //error_log(__METHOD__." $cookiename->$cookievalue".' returned:'.print_r($rv,true).print_r($_COOKIE,true)); } /** @@ -1293,6 +1284,7 @@ class egw_session self::$cookie_domain = isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']; } // remove port from HTTP_HOST + $arr = null; if (preg_match("/^(.*):(.*)$/",self::$cookie_domain,$arr)) { self::$cookie_domain = $arr[1]; @@ -1305,11 +1297,11 @@ class egw_session if (!$GLOBALS['egw_info']['server']['cookiepath'] || !(self::$cookie_path = parse_url($GLOBALS['egw_info']['server']['webserver_url'],PHP_URL_PATH))) { - self::$cookie_path = '/'; + self::$cookie_path = '/'; } //echo "

cookie_path='self::$cookie_path', cookie_domain='self::$cookie_domain'

\n"; - session_set_cookie_params(0, $path, $domain, + session_set_cookie_params(0, self::$cookie_path, self::$cookie_domain, // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) empty($GLOBALS['egw_info']['server']['insecure_cookies']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', true); } @@ -1320,8 +1312,8 @@ class egw_session * @param string $login on login $_POST['login'], $_SERVER['PHP_AUTH_USER'] or $_SERVER['REMOTE_USER'] * @param string $domain_requested usually self::get_request('domain') * @param string &$default_domain usually $default_domain get's set eg. by sitemgr - * @param string|array $server_name usually array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) - * @param array $domains=null defaults to $GLOBALS['egw_domain'] from the header + * @param string|array $server_names usually array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) + * @param array $domains =null defaults to $GLOBALS['egw_domain'] from the header * @return string $GLOBALS['egw_info']['user']['domain'] set with the domain/instance to use */ public static function search_instance($login,$domain_requested,&$default_domain,$server_names,array $domains=null) @@ -1342,9 +1334,9 @@ class egw_session } else { - $domain_part = explode('.',$server_name); - array_shift($domain_part); - $domain_part = implode('.',$domain_part); + $parts = explode('.', $server_name); + array_shift($parts); + $domain_part = implode('.', $parts); if(isset($domains[$domain_part])) { $default_domain = $domain_part; @@ -1385,7 +1377,7 @@ class egw_session /** * Update session_action and session_dla (session last used time) * - * @param boolean $update_access_log=true false: dont update egw_access_log table + * @param boolean $update_access_log =true false: dont update egw_access_log table * @return string action as written to egw_access_log.session_action */ private function update_dla($update_access_log=true) @@ -1538,7 +1530,7 @@ class egw_session * * @param string $user username * @param string $password password or md5 hash of password if $allow_password_md5 - * @param boolean $allow_password_md5=false can password alread be an md5 hash + * @param boolean $allow_password_md5 =false can password alread be an md5 hash * @return string */ static function user_pw_hash($user,$password,$allow_password_md5=false) @@ -1598,7 +1590,7 @@ class egw_session * - true --> private caching by browser (no expires header) * - "public" or integer --> public caching with given cache_expire in minutes or php.ini default session_cache_expire * - * @param int $expire=null expiration time in seconds, default $GLOBALS['egw_info']['flags']['nocachecontrol'] or php.ini session.cache_expire + * @param int $expire =null expiration time in seconds, default $GLOBALS['egw_info']['flags']['nocachecontrol'] or php.ini session.cache_expire */ public static function cache_control($expire=null) { @@ -1635,6 +1627,7 @@ class egw_session if ($expire && (session_cache_limiter() !== ($expire===true?'private_no_expire':'public') || is_int($expire) && $expire/60 !== session_cache_expire())) { + $file = $line = null; if (headers_sent($file, $line)) { error_log(__METHOD__."($expire) called, but header already sent in $file: $line"); @@ -1667,10 +1660,10 @@ class egw_session * Get a session list (of the current instance) * * @param int $start - * @param string $sort='DESC' ASC or DESC - * @param string $order='session_dla' session_lid, session_id, session_started, session_logintime, session_action, or (default) session_dla - * @param boolean $all_no_sort=False skip sorting and limiting to maxmatchs if set to true - * @param array $filter=array() extra filter for sessions + * @param string $sort ='DESC' ASC or DESC + * @param string $order ='session_dla' session_lid, session_id, session_started, session_logintime, session_action, or (default) session_dla + * @param boolean $all_no_sort =False skip sorting and limiting to maxmatchs if set to true + * @param array $filter =array() extra filter for sessions * @return array with sessions (values for keys as in $sort) */ public static function session_list($start,$sort='DESC',$order='session_dla',$all_no_sort=False,array $filter=array()) @@ -1758,6 +1751,6 @@ class egw_session */ function delete_cache($accountid='') { - + unset($accountid); // not used, but required by function signature } } diff --git a/phpgwapi/inc/class.egw_sharing.inc.php b/phpgwapi/inc/class.egw_sharing.inc.php index e59ed173c4..4ad4d71e99 100644 --- a/phpgwapi/inc/class.egw_sharing.inc.php +++ b/phpgwapi/inc/class.egw_sharing.inc.php @@ -15,6 +15,12 @@ * * Token generation uses openssl_random_pseudo_bytes, if available, otherwise * mt_rand based auth::randomstring is used. + * + * @todo UI to create shares + * @todo handle existing user sessions eg. by mounting share under it's token into vfs and redirect to regular filemanager + * @todo handle mounts inside shared directory (they get currently lost) + * @todo handle absolute symlinks (wont work as we use share as root) + * @todo use sharing instead of attachments in mail app */ class egw_sharing { @@ -33,20 +39,31 @@ class egw_sharing * * @var egw_db */ - protected $db; + protected static $db; /** - * Constructor + * Share we are instanciated for + * + * @var array */ - public function __construct() + protected $share; + + /** + * Protected constructor called via self::create_session + * + * @param string $token + * @param array $share + */ + protected function __construct(array $share) { - $this->db = $GLOBALS['egw']->db; + self::$db = $GLOBALS['egw']->db; + $this->share = $share; } /** - * Server a request on a share specified in REQUEST_URI + * Init sharing by setting PHP_AUTH_USER from token in url */ - public function ServeRequest() + public static function init() { // WebDAV has no concept of a query string and clients (including cadaver) // seem to pass '?' unencoded, so we need to extract the path info out @@ -60,27 +77,62 @@ class egw_sharing $path_info = substr($path_info, strlen($_SERVER['SCRIPT_NAME'])); list(, $token/*, $path*/) = explode('/', $path_info, 3); - if (empty($token) || !($share = $this->db->select(self::TABLE, '*', array( + $_SERVER['PHP_AUTH_USER'] = $token; + if (!isset($_SERVER['PHP_AUTH_PW'])) $_SERVER['PHP_AUTH_PW'] = ''; + + return $token; + } + + /** + * Create sharing session + * + * @return string with sessionid, does NOT return if no session created + */ + public static function create_session() + { + self::$db = $GLOBALS['egw']->db; + + $token = $_SERVER['PHP_AUTH_USER']; + + if (empty($token) || !($share = self::$db->select(self::TABLE, '*', array( 'share_token' => $token, - '(share_expires IS NULL OR share_expires > '.$this->db->quote(time(), 'date').')', - ), __LINE__, __FILE__)->fetch())) + '(share_expires IS NULL OR share_expires > '.self::$db->quote(time(), 'date').')', + ), __LINE__, __FILE__)->fetch()) || + !$GLOBALS['egw']->accounts->exists($share['share_owner'])) { sleep(1); $status = '404 Not Found'; header("HTTP/1.1 $status"); header("X-WebDAV-Status: $status", true); - echo "Requested resource '".htmlspecialchars($path_info)."' does NOT exist!\n"; + echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n"; common::egw_exit(); } + // ToDo: password check, if required + + // create session without checking auth: create(..., false, false) + if (!($sessionid = $GLOBALS['egw']->session->create('anonymous', '', 'text', false, false))) + { + sleep(1); + $status = '500 Internal Server Error'; + header("HTTP/1.1 $status"); + header("X-WebDAV-Status: $status", true); + echo "Failed to create session: ".$GLOBALS['egw']->session->reason."\n"; + common::egw_exit(); + } + // only allow filemanager app + $GLOBALS['egw_info']['user']['apps'] = array( + 'filemanager' => $GLOBALS['egw_info']['apps']['filemanager'] + ); + $share['resolve_url'] = egw_vfs::resolve_url($share['share_path']); // if share not writable append ro=1 to mount url to make it readonly - if (!$this->db->from_bool($share['share_writable'])) + if (!self::$db->from_bool($share['share_writable'])) { $share['resolve_url'] .= (strpos($share['resolve_url'], '?') ? '&' : '?').'ro=1'; } //_debug_array($share); - // arrange vfs to only contain shared url + // arrange vfs to only contain shared url and use share-owner as user egw_vfs::$is_root = true; if (!egw_vfs::mount($share['resolve_url'], '/', false, false, true)) { @@ -88,17 +140,49 @@ class egw_sharing $status = '404 Not Found'; header("HTTP/1.1 $status"); header("X-WebDAV-Status: $status", true); - echo "Requested resource '".htmlspecialchars($path_info)."' does NOT exist!\n"; + echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n"; common::egw_exit(); } egw_vfs::$is_root = false; - egw_vfs::$user = $GLOBALS['egw_info']['user']['account_id'] = $share['share_owner']; + // need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV + $GLOBALS['egw_info']['server']['vfs_fstab'] = egw_vfs::mount(); + $GLOBALS['egw_info']['user']['vfs_user'] = egw_vfs::$user = $share['share_owner']; egw_vfs::clearstatcache(); - // ToDo: password and write protection - //$GLOBALS['egw']->session->commit_session(); - $webdav_server = new vfs_webdav_server(); - $webdav_server->ServeRequest('/'.$token); + // update accessed timestamp + self::$db->update(self::TABLE, array( + 'share_last_accessed' => $share['share_last_accessed']=time(), + ), array( + 'share_id' => $share['share_id'], + ), __LINE__, __FILE__); + + // store sharing object in egw object and therefore in session + $GLOBALS['egw']->sharing = new egw_sharing($share); + + return $sessionid; + } + + /** + * Server a request on a share specified in REQUEST_URI + */ + public function ServeRequest() + { + // use pure WebDAV for everything but GET requests to directories + if (!egw_vfs::is_dir('/') || $_SERVER['REQUEST_METHOD'] != 'GET' || + // or unsupported browsers like ie < 10 + html::$user_agent == 'msie' && html::$ua_version < 10.0 || + // or if no filemanager installed (WebDAV has own autoindex) + !file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php')) + { + //$GLOBALS['egw']->session->commit_session(); + $webdav_server = new vfs_webdav_server(); + $webdav_server->ServeRequest('/'.$this->share['share_token']); + return; + } + // run full eTemplate2 UI for directories + $_GET['path'] = '/'; + $ui = new egw_sharing_filemanager(); + $ui->index(); } /** @@ -115,4 +199,22 @@ class egw_sharing return $token; } +} + +if (file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php')) +{ + require_once __DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php'; + + class egw_sharing_filemanager extends filemanager_ui + { + /** + * Get the configured start directory for the current user + * + * @return string + */ + static function get_home_dir() + { + return '/'; + } + } } \ No newline at end of file diff --git a/phpgwapi/inc/class.egw_vfs.inc.php b/phpgwapi/inc/class.egw_vfs.inc.php index 858c42b23a..b72c52fc3e 100644 --- a/phpgwapi/inc/class.egw_vfs.inc.php +++ b/phpgwapi/inc/class.egw_vfs.inc.php @@ -1774,7 +1774,9 @@ class egw_vfs extends vfs_stream_wrapper */ static function init_static() { - self::$user = (int) $GLOBALS['egw_info']['user']['account_id']; + // if special user/vfs_user given (eg. from sharing) use it instead default user/account_id + self::$user = (int)(isset($GLOBALS['egw_info']['user']['vfs_user']) ? + $GLOBALS['egw_info']['user']['vfs_user'] : $GLOBALS['egw_info']['user']['account_id']); self::$is_admin = isset($GLOBALS['egw_info']['user']['apps']['admin']); self::$db = isset($GLOBALS['egw_setup']->db) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db; self::$lock_cache = array(); diff --git a/phpgwapi/js/jsapi/egw_links.js b/phpgwapi/js/jsapi/egw_links.js index ed219e303e..a4f906e733 100644 --- a/phpgwapi/js/jsapi/egw_links.js +++ b/phpgwapi/js/jsapi/egw_links.js @@ -26,7 +26,7 @@ egw.extend('links', egw.MODULE_GLOBAL, function() * * @access: private, use egw.open() or egw.set_link_registry() */ - var link_registry = null; + var link_registry = undefined; /** * Local cache for link-titles diff --git a/phpgwapi/lang.php b/phpgwapi/lang.php index 6c7f303972..da1218a847 100644 --- a/phpgwapi/lang.php +++ b/phpgwapi/lang.php @@ -20,7 +20,7 @@ ini_set('zlib.output_compression', 0); $GLOBALS['egw_info'] = array( 'flags' => array( - 'currentapp' => in_array($_GET['app'],array('etemplate','common','custom')) ? 'home' : $_GET['app'], + 'currentapp' => 'home', 'noheader' => true, 'load_translations' => false, // do not automatically load translations 'nocachecontrol' => true, diff --git a/share.php b/share.php index 558f8d79ec..a0cee734bf 100644 --- a/share.php +++ b/share.php @@ -10,16 +10,26 @@ * @version $Id$ */ +require_once('./phpgwapi/inc/class.egw_sharing.inc.php'); + +// set PHP_AUTH_USER from token in URL, must be called before header include, to be able to verify session +egw_sharing::init(); + $GLOBALS['egw_info'] = array( 'flags' => array( - 'disable_Template_class' => True, - 'noheader' => True, - 'currentapp' => 'login', + 'disable_Template_class' => true, + 'noheader' => true, + 'nonavbar' => 'always', // true would cause eTemplate to reset it to false for non-popups! + 'currentapp' => 'filemanager', + 'autocreate_session_callback' => 'egw_sharing::create_session', 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) ) ); include('./header.inc.php'); -$sharing = new egw_sharing(); -$sharing->ServeRequest(); \ No newline at end of file +if (!$GLOBALS['egw']->sharing) +{ + egw_sharing::create_session(); +} +$GLOBALS['egw']->sharing->ServeRequest();