moving egw_digest_auth, vfs_webdav_server and egw_sharing to new api

This commit is contained in:
Ralf Becker 2016-03-20 16:19:53 +00:00
parent 3692f01100
commit 67cb60b972
18 changed files with 477 additions and 420 deletions

View File

@ -0,0 +1,274 @@
<?php
/**
* EGroupware API: Basic and Digest Auth
*
* For Apache FCGI you need the following rewrite rule:
*
* RewriteEngine on
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
*
* Otherwise authentication request will be send over and over again, as password is NOT available to PHP!
* (This makes authentication details available in PHP as $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage header
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2010-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Header;
use EGroupware\Api;
/**
* Class to authenticate via basic or digest auth
*
* The more secure digest auth requires:
* a) cleartext passwords in SQL table
* b) md5 hashes of username, realm, password stored somewhere (NOT yet implemented)
* Otherwise digest auth is not possible and therefore not offered to the client.
*
* Usage example:
*
* $GLOBALS['egw_info']['flags'] = array(
* 'noheader' => True,
* 'currentapp' => 'someapp',
* 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
* 'autocreate_session_callback' => 'EGroupware\\Api\\Header\\Authenticate::autocreate_session_callback',
* 'auth_realm' => 'EGroupware',
* );
* include(dirname(__FILE__).'/header.inc.php');
*
* @link http://www.php.net/manual/en/features.http-auth.php
* @ToDo check if we have to check if returned nonce matches our challange (not done in above link, but why would it be there)
* @link http://en.wikipedia.org/wiki/Digest_access_authentication
* @link http://tools.ietf.org/html/rfc2617
*
* Commented out is accept-charset parameter from (seems not supported by any client I tested with)
* @link https://tools.ietf.org/id/draft-reschke-basicauth-enc-06.html
*
* Implemented support for clients sending credentials in iso-8859-1 instead of our utf-8:
* - Firefox 19.0
* - Thunderbird 17.0.3 with Lightning 1.8
* - IE 8
* - Netdrive
* (Chrome 24 or Safari 6 sends credentials in charset of webpage.)
*/
class Authenticate
{
/**
* Log to error_log:
* 0 = dont
* 1 = no cleartext passwords
* 2 = all
*/
const ERROR_LOG = 0;
/**
* Callback to be used to create session via header include authenticated via basic or digest auth
*
* @param array $account NOT used!
* @return string valid session-id or does NOT return at all!
*/
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'] : '**********';
error_log(__METHOD__.'() PHP_AUTH_USER='.array2string($_SERVER['PHP_AUTH_USER']).', PHP_AUTH_PW='.array2string($pw).', PHP_AUTH_DIGEST='.array2string($_SERVER['PHP_AUTH_DIGEST']));
}
$realm = $GLOBALS['egw_info']['flags']['auth_realm'];
if (empty($realm)) $realm = 'EGroupware';
$username = $_SERVER['PHP_AUTH_USER']; $password = $_SERVER['PHP_AUTH_PW'];
// Support for basic auth when using PHP CGI (what about digest auth?)
if (!isset($username) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'],'Basic ') === 0)
{
$hash = base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'],6));
if (strpos($hash, ':') !== false)
{
list($username, $password) = explode(':', $hash, 2);
}
}
elseif (isset($_SERVER['PHP_AUTH_DIGEST']) && !self::is_valid($realm,$_SERVER['PHP_AUTH_DIGEST'],$username,$password))
{
unset($password);
}
// if given password contains non-ascii chars AND we can not authenticate with it
if (isset($username) && isset($password) &&
(preg_match('/[^\x20-\x7F]/', $password) || strpos($password, '\\x') !== false) &&
!$GLOBALS['egw']->auth->authenticate($username, $password, 'text'))
{
self::decode_password($password);
}
// 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;
header('WWW-Authenticate: Basic realm="'.$realm.'"');// draft-reschke-basicauth-enc-06 adds, accept-charset="'.translation::charset().'"');
self::digest_header($realm);
header('HTTP/1.1 401 Unauthorized');
header('X-WebDAV-Status: 401 Unauthorized', true);
echo "<html>\n<head>\n<title>401 Unauthorized</title>\n<body>\nAuthorization failed.\n</body>\n</html>\n";
exit;
}
return $sessionid;
}
/**
* Decode password containing non-ascii chars
*
* @param string &$password
* @return boolean true if conversation happend, false if there was no need for a conversation
*/
public static function decode_password(&$password)
{
// if given password contains non-ascii chars AND we can not authenticate with it
if (preg_match('/[^\x20-\x7F]/', $password) || strpos($password, '\\x') !== false)
{
// replace \x encoded non-ascii chars in password, as they are used eg. by Thunderbird for German umlauts
if (strpos($password, '\\x') !== false)
{
$password = preg_replace_callback('/\\\\x([0-9A-F]{2})/i', function($matches){
return chr(hexdec($matches[1]));
}, $password);
}
// try translating the password from iso-8859-1 to utf-8
$password = Api\Translation::convert($password, 'iso-8859-1');
//error_log(__METHOD__."() Fixed non-ascii password of user '$username' from '$_SERVER[PHP_AUTH_PW]' to '$password'");
return true;
}
return false;
}
/**
* Check if digest auth is available for a given realm (and user): do we use cleartext passwords
*
* If no user is given, check is NOT authoritative, 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
* @return boolean true if digest auth is available, false otherwise
*/
static public function digest_auth_available($realm,$username=null,&$user_pw=null)
{
// we currently require plaintext passwords!
if (!($GLOBALS['egw_info']['server']['auth_type'] == 'sql' && $GLOBALS['egw_info']['server']['sql_encryption_type'] == 'plain') ||
$GLOBALS['egw_info']['server']['auth_type'] == 'ldap' && $GLOBALS['egw_info']['server']['ldap_encryption_type'] == 'plain')
{
if (self::ERROR_LOG) error_log(__METHOD__."('$username') return false (no plaintext passwords used)");
return false; // no plain-text passwords used
}
// check for specific user, if given
if (!is_null($username) && !(($user_pw = $GLOBALS['egw']->accounts->id2name($username,'account_pwd','u')) ||
$GLOBALS['egw_info']['server']['auth_type'] == 'sql' && substr($user_pw,0,7) != '{PLAIN}'))
{
unset($user_pw);
if (self::ERROR_LOG) error_log(__METHOD__."('$realm','$username') return false (unknown user or NO plaintext password for user)");
return false; // user does NOT exist, or has no plaintext passwords (ldap server requires real root_dn or special ACL!)
}
if (substr($user_pw,0,7) == '{PLAIN}') $user_pw = substr($user_pw,7);
if (self::ERROR_LOG)
{
$pw = self::ERROR_LOG > 1 ? $user_pw : '**********';
error_log(__METHOD__."('$realm','$username','$pw') return true");
}
return true;
}
/**
* Send header offering digest auth, if it's generally available
*
* @param string $realm
* @param string &$nonce=null on return
*/
static public function digest_header($realm,&$nonce=null)
{
if (self::digest_auth_available($realm))
{
$nonce = uniqid();
header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.$nonce.'",opaque="'.md5($realm).'"');
if (self::ERROR_LOG) error_log(__METHOD__."() offering digest auth for realm '$realm' using nonce='$nonce'");
}
}
/**
* Check digest
*
* @param string $realm
* @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
*/
static public function is_valid($realm,$auth_digest=null,&$username=null,&$password=null)
{
if (is_null($auth_digest)) $auth_digest = $_SERVER['PHP_AUTH_DIGEST'];
$data = self::parse_digest($auth_digest);
if (!$data || !($A1 = self::get_digest_A1($realm,$username=$data['username'],$password=null)))
{
error_log(__METHOD__."('$realm','$auth_digest','$username') returning FALSE");
return false;
}
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if (self::ERROR_LOG) error_log(__METHOD__."('$realm','$auth_digest','$username') response='$data[response]', valid_response='$valid_response' returning ".array2string($data['response'] === $valid_response));
return $data['response'] === $valid_response;
}
/**
* Calculate the A1 digest hash
*
* @param string $realm
* @param string $username
* @param string &$password=null password to use or if null, on return stored password
* @return string|boolean false if $password not given and can NOT be read
*/
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;
}
if (is_null($password)) $password = $user_pw;
$A1 = md5($username . ':' . $realm . ':' . $password);
if (self::ERROR_LOG > 1) error_log(__METHOD__."('$realm','$username','$password') returning ".array2string($A1));
return $A1;
}
/**
* Parse the http auth header
*/
static public function parse_digest($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$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)
{
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
//error_log(__METHOD__."('$txt') returning ".array2string($needed_parts ? false : $data));
return $needed_parts ? false : $data;
}
}

View File

@ -24,7 +24,6 @@ namespace EGroupware\Api;
// explicitly reference classes still in phpgwapi // explicitly reference classes still in phpgwapi
use egw_mailer; use egw_mailer;
use egw_digest_auth; // egw_digest_auth::parse_digest
/** /**
* Create, verifies or destroys an EGroupware session * Create, verifies or destroys an EGroupware session
@ -831,7 +830,7 @@ class Session
{ {
// we generate a pseudo-sessionid from the digest username, realm and nounce // 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) // can't use full $_SERVER['PHP_AUTH_DIGEST'], as it changes (contains eg. the url)
$data = egw_digest_auth::parse_digest($_SERVER['PHP_AUTH_DIGEST']); $data = Header\Authenticate::parse_digest($_SERVER['PHP_AUTH_DIGEST']);
$sessionid = md5($data['username'].':'.$data['realm'].':'.$data['nonce'].':'.$_SERVER['HTTP_HOST']. $sessionid = md5($data['username'].':'.$data['realm'].':'.$data['nonce'].':'.$_SERVER['HTTP_HOST'].
EGW_SERVER_ROOT.':'.self::getuser_ip().':'.filemtime(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php'). EGW_SERVER_ROOT.':'.self::getuser_ip().':'.filemtime(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php').
':'.$_SERVER['HTTP_USER_AGENT']); ':'.$_SERVER['HTTP_USER_AGENT']);

View File

@ -5,16 +5,30 @@
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api * @package api
* @subpackage Vfs
* @author Ralf Becker <rb@stylite.de> * @author Ralf Becker <rb@stylite.de>
* @copyright (c) 2014/15 by Ralf Becker <rb@stylite.de> * @copyright (c) 2014-16 by Ralf Becker <rb@stylite.de>
* @version $Id$ * @version $Id$
*/ */
namespace EGroupware\Api\Vfs;
use EGroupware\Api;
use EGroupware\Api\Vfs;
// explicitly list old, not yet ported api classes
use common; // egw_exist
use egw_framework;
use asyncservice;
use egw; // link
use filemanager_ui;
/** /**
* VFS sharing * VFS sharing
* *
* Token generation uses openssl_random_pseudo_bytes, if available, otherwise * Token generation uses openssl_random_pseudo_bytes, if available, otherwise
* mt_rand based auth::randomstring is used. * mt_rand based Api\Auth::randomstring is used.
* *
* Existing user sessions are kept whenever possible by an additional mount into regular VFS: * Existing user sessions are kept whenever possible by an additional mount into regular VFS:
* - share owner is current user (no problems with rights, they simply match) * - share owner is current user (no problems with rights, they simply match)
@ -26,7 +40,7 @@
* @todo handle mounts inside shared directory (they get currently lost) * @todo handle mounts inside shared directory (they get currently lost)
* @todo handle absolute symlinks (wont work as we use share as root) * @todo handle absolute symlinks (wont work as we use share as root)
*/ */
class egw_sharing class Sharing
{ {
/** /**
* Length of base64 encoded token (real length is only 3/4 of it) * Length of base64 encoded token (real length is only 3/4 of it)
@ -184,9 +198,9 @@ class egw_sharing
// check password, if required // check password, if required
if ($share['share_passwd'] && (empty($_SERVER['PHP_AUTH_PW']) || if ($share['share_passwd'] && (empty($_SERVER['PHP_AUTH_PW']) ||
!(auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt') || !(Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt') ||
egw_digest_auth::decode_password($_SERVER['PHP_AUTH_PW']) && Api\Header\Authenticate::decode_password($_SERVER['PHP_AUTH_PW']) &&
auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt')))) Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt'))))
{ {
$realm = 'EGroupware share '.$share['share_token']; $realm = 'EGroupware share '.$share['share_token'];
header('WWW-Authenticate: Basic realm="'.$realm.'"'); header('WWW-Authenticate: Basic realm="'.$realm.'"');
@ -201,10 +215,10 @@ class egw_sharing
if (count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1) if (count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1)
{ {
unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount() unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount()
$GLOBALS['egw_info']['server']['vfs_fstab'] = egw_vfs::mount(); $GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
egw_vfs::clearstatcache(); Vfs::clearstatcache();
} }
$share['resolve_url'] = egw_vfs::resolve_url($share['share_path'], true, true, true, true); // true = fix evtl. contained url parameter $share['resolve_url'] = Vfs::resolve_url($share['share_path'], true, true, true, true); // true = fix evtl. contained url parameter
// if share not writable append ro=1 to mount url to make it readonly // if share not writable append ro=1 to mount url to make it readonly
if (!self::$db->from_bool($share['share_writable'])) if (!self::$db->from_bool($share['share_writable']))
{ {
@ -217,7 +231,7 @@ class egw_sharing
$share['share_root'] = '/'.$share['share_token']; $share['share_root'] = '/'.$share['share_token'];
// if current user is not the share owner, we cant just mount share // if current user is not the share owner, we cant just mount share
if (egw_vfs::$user != $share['share_owner']) if (Vfs::$user != $share['share_owner'])
{ {
$keep_session = false; $keep_session = false;
} }
@ -230,12 +244,12 @@ class egw_sharing
); );
$share['share_root'] = '/'; $share['share_root'] = '/';
egw_vfs::$user = $share['share_owner']; Vfs::$user = $share['share_owner'];
} }
// mounting share // mounting share
egw_vfs::$is_root = true; Vfs::$is_root = true;
if (!egw_vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session)) if (!Vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session))
{ {
sleep(1); sleep(1);
$status = '404 Not Found'; $status = '404 Not Found';
@ -244,8 +258,8 @@ class egw_sharing
echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n"; echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
common::egw_exit(); common::egw_exit();
} }
egw_vfs::$is_root = false; Vfs::$is_root = false;
egw_vfs::clearstatcache(); Vfs::clearstatcache();
// update accessed timestamp // update accessed timestamp
self::$db->update(self::TABLE, array( self::$db->update(self::TABLE, array(
@ -255,7 +269,7 @@ class egw_sharing
), __LINE__, __FILE__); ), __LINE__, __FILE__);
// store sharing object in egw object and therefore in session // store sharing object in egw object and therefore in session
$GLOBALS['egw']->sharing = new egw_sharing($share); $GLOBALS['egw']->sharing = new Sharing($share);
// we have a session we want to keep, but share owner is different from current user and we need filemanager UI, or no session // we have a session we want to keep, but share owner is different from current user and we need filemanager UI, or no session
// --> create a new anon session // --> create a new anon session
@ -284,16 +298,16 @@ class egw_sharing
$GLOBALS['egw']->session->commit_session(); $GLOBALS['egw']->session->commit_session();
} }
// need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV // need to store new fstab and vfs_user in session to allow GET requests / downloads via WebDAV
$GLOBALS['egw_info']['user']['vfs_user'] = egw_vfs::$user; $GLOBALS['egw_info']['user']['vfs_user'] = Vfs::$user;
$GLOBALS['egw_info']['server']['vfs_fstab'] = egw_vfs::mount(); $GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
// update modified egw and egw_info again in session, if neccessary // update modified egw and egw_info again in session, if neccessary
if ($keep_session || $sessionid) if ($keep_session || $sessionid)
{ {
$_SESSION[egw_session::EGW_INFO_CACHE] = $GLOBALS['egw_info']; $_SESSION[Api\Session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
unset($_SESSION[egw_session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request unset($_SESSION[Api\Session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request
$_SESSION[egw_session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']); $_SESSION[Api\Session::EGW_OBJECT_CACHE] = serialize($GLOBALS['egw']);
} }
return $sessionid; return $sessionid;
@ -308,9 +322,9 @@ class egw_sharing
*/ */
public function use_filemanager() public function use_filemanager()
{ {
return !(!egw_vfs::is_dir($this->share['share_root']) || $_SERVER['REQUEST_METHOD'] != 'GET' || return !(!Vfs::is_dir($this->share['share_root']) || $_SERVER['REQUEST_METHOD'] != 'GET' ||
// or unsupported browsers like ie < 10 // or unsupported browsers like ie < 10
html::$user_agent == 'msie' && html::$ua_version < 10.0 || Api\Header\UserAgent::type() == 'msie' && Api\Header\UserAgent::version() < 10.0 ||
// or if no filemanager installed (WebDAV has own autoindex) // or if no filemanager installed (WebDAV has own autoindex)
!file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php')); !file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php'));
} }
@ -331,13 +345,13 @@ class egw_sharing
if (!$this->use_filemanager()) if (!$this->use_filemanager())
{ {
// send a content-disposition header, so browser knows how to name downloaded file // send a content-disposition header, so browser knows how to name downloaded file
if (!egw_vfs::is_dir($this->share['share_root'])) if (!Vfs::is_dir($this->share['share_root']))
{ {
html::content_disposition_header(egw_vfs::basename($this->share['share_path']), false); Api\Header\Content::disposition(Vfs::basename($this->share['share_path']), false);
} }
//$GLOBALS['egw']->session->commit_session(); //$GLOBALS['egw']->session->commit_session();
$webdav_server = new vfs_webdav_server(); $webdav_server = new Vfs\WebDAV();
$webdav_server->ServeRequest(egw_vfs::concat($this->share['share_root'], $this->share['share_token'])); $webdav_server->ServeRequest(Vfs::concat($this->share['share_root'], $this->share['share_token']));
return; return;
} }
// run full eTemplate2 UI for directories // run full eTemplate2 UI for directories
@ -346,7 +360,7 @@ class egw_sharing
$_GET['cd'] = 'no'; $_GET['cd'] = 'no';
$GLOBALS['egw_info']['flags']['js_link_registry'] = true; $GLOBALS['egw_info']['flags']['js_link_registry'] = true;
egw_framework::includeCSS('filemanager', 'sharing'); egw_framework::includeCSS('filemanager', 'sharing');
$ui = new egw_sharing_filemanager(); $ui = new SharingUi();
$ui->index(); $ui->index();
} }
@ -357,13 +371,13 @@ class egw_sharing
*/ */
public static function token() public static function token()
{ {
// generate random token (using oppenssl if available otherwise mt_rand based auth::randomstring) // generate random token (using oppenssl if available otherwise mt_rand based Api\Auth::randomstring)
do { do {
$token = function_exists('openssl_random_pseudo_bytes') ? $token = function_exists('openssl_random_pseudo_bytes') ?
base64_encode(openssl_random_pseudo_bytes(3*self::TOKEN_LENGTH/4)) : base64_encode(openssl_random_pseudo_bytes(3*self::TOKEN_LENGTH/4)) :
auth::randomstring(self::TOKEN_LENGTH); Api\Auth::randomstring(self::TOKEN_LENGTH);
// base64 can contain chars not allowed in our vfs-urls eg. / or # // base64 can contain chars not allowed in our vfs-urls eg. / or #
} while ($token != egw_vfs::encodePathComponent($token)); } while ($token != Vfs::encodePathComponent($token));
return $token; return $token;
} }
@ -387,7 +401,7 @@ class egw_sharing
if (empty($name)) $name = $path; if (empty($name)) $name = $path;
$path2tmp =& egw_cache::getSession(__CLASS__, 'path2tmp'); $path2tmp =& Api\Cache::getSession(__CLASS__, 'path2tmp');
// allow filesystem path only for temp_dir // allow filesystem path only for temp_dir
$temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/'; $temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/';
@ -402,13 +416,13 @@ class egw_sharing
{ {
$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path; $path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
} }
$vfs_path = egw_vfs::parse_url($path, PHP_URL_PATH); $vfs_path = Vfs::parse_url($path, PHP_URL_PATH);
$exists = egw_vfs::file_exists($vfs_path) && egw_vfs::is_readable($vfs_path); $exists = Vfs::file_exists($vfs_path) && Vfs::is_readable($vfs_path);
} }
// check if file exists and is readable // check if file exists and is readable
if (!$exists) if (!$exists)
{ {
throw new egw_exception_not_found("'$path' NOT found!"); throw new Api\Exception\NotFound("'$path' NOT found!");
} }
// check if file has been shared before, with identical attributes // check if file has been shared before, with identical attributes
if (($mode != self::LINK || isset($path2tmp[$path])) && if (($mode != self::LINK || isset($path2tmp[$path])) &&
@ -447,29 +461,29 @@ class egw_sharing
if ($mode == 'link') if ($mode == 'link')
{ {
$user_tmp = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp'; $user_tmp = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp';
if (!egw_vfs::file_exists($user_tmp) && !egw_vfs::mkdir($user_tmp)) if (!Vfs::file_exists($user_tmp) && !Vfs::mkdir($user_tmp))
{ {
throw new egw_exception_assertion_failed("Could NOT create temp. directory '$user_tmp'!"); throw new Api\Exception\AssertionFailed("Could NOT create temp. directory '$user_tmp'!");
} }
$n = 0; $n = 0;
do { do {
$tmp_file = egw_vfs::concat($user_tmp, ($n?$n.'.':'').egw_vfs::basename($name)); $tmp_file = Vfs::concat($user_tmp, ($n?$n.'.':'').Vfs::basename($name));
} }
while(!(is_dir($path) && egw_vfs::mkdir($tmp_file) || while(!(is_dir($path) && Vfs::mkdir($tmp_file) ||
!is_dir($path) && (!egw_vfs::file_exists($tmp_file) && ($fp = egw_vfs::fopen($tmp_file, 'x')) || !is_dir($path) && (!Vfs::file_exists($tmp_file) && ($fp = Vfs::fopen($tmp_file, 'x')) ||
// do not copy identical files again to users tmp dir, just re-use them // do not copy identical files again to users tmp dir, just re-use them
egw_vfs::file_exists($tmp_file) && egw_vfs::compare(egw_vfs::PREFIX.$tmp_file, $path))) && $n++ < 100); Vfs::file_exists($tmp_file) && Vfs::compare(Vfs::PREFIX.$tmp_file, $path))) && $n++ < 100);
if ($n >= 100) if ($n >= 100)
{ {
throw new egw_exception_assertion_failed("Could NOT create temp. file '$tmp_file'!"); throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
} }
if ($fp) fclose($fp); if ($fp) fclose($fp);
if (is_dir($path) && !egw_vfs::copy_files(array($path), $tmp_file) || if (is_dir($path) && !Vfs::copy_files(array($path), $tmp_file) ||
!is_dir($path) && !copy($path, egw_vfs::PREFIX.$tmp_file)) !is_dir($path) && !copy($path, Vfs::PREFIX.$tmp_file))
{ {
throw new egw_exception_assertion_failed("Could NOT create temp. file '$tmp_file'!"); throw new Api\Exception\AssertionFailed("Could NOT create temp. file '$tmp_file'!");
} }
// store temp. path in session, to be able to add more recipients // store temp. path in session, to be able to add more recipients
$path2tmp[$path] = $tmp_file; $path2tmp[$path] = $tmp_file;
@ -490,7 +504,7 @@ class egw_sharing
try { try {
self::$db->insert(self::TABLE, $share = array( self::$db->insert(self::TABLE, $share = array(
'share_token' => self::token(), 'share_token' => self::token(),
'share_path' => egw_vfs::parse_url($path, PHP_URL_PATH), 'share_path' => Vfs::parse_url($path, PHP_URL_PATH),
'share_owner' => $GLOBALS['egw_info']['user']['account_id'], 'share_owner' => $GLOBALS['egw_info']['user']['account_id'],
'share_with' => implode(',', (array)$recipients), 'share_with' => implode(',', (array)$recipients),
'share_created' => time(), 'share_created' => time(),
@ -499,7 +513,7 @@ class egw_sharing
$share['share_id'] = self::$db->get_last_insert_id(self::TABLE, 'share_id'); $share['share_id'] = self::$db->get_last_insert_id(self::TABLE, 'share_id');
break; break;
} }
catch(egw_exception_db $e) { catch(Api\Db\Exception $e) {
if ($i++ > 3) throw $e; if ($i++ > 3) throw $e;
unset($e); unset($e);
} }
@ -509,9 +523,9 @@ class egw_sharing
} }
/** /**
* so_sql instance for egw_sharing table * Api\Storage\Base instance for egw_sharing table
* *
* @var so_sql * @var Api\Storage\Base
*/ */
protected static $so; protected static $so;
@ -522,7 +536,7 @@ class egw_sharing
{ {
if (!isset(self::$so)) if (!isset(self::$so))
{ {
self::$so = new so_sql('phpgwapi', self::TABLE, null, '', true); self::$so = new Api\Storage\Base('phpgwapi', self::TABLE, null, '', true);
self::$so->set_times('string'); self::$so->set_times('string');
} }
return self::$so; return self::$so;
@ -567,7 +581,7 @@ class egw_sharing
// if not delete them // if not delete them
foreach($tmp_paths as $path) foreach($tmp_paths as $path)
{ {
egw_vfs::remove($path); Vfs::remove($path);
} }
} }
return $deleted; return $deleted;
@ -586,7 +600,7 @@ class egw_sharing
public static function tmp_cleanup() public static function tmp_cleanup()
{ {
if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db; if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
egw_vfs::$is_root = true; Vfs::$is_root = true;
try { try {
$cols = array( $cols = array(
@ -607,7 +621,7 @@ class egw_sharing
"share_path LIKE '/home/%/.tmp/%'", "share_path LIKE '/home/%/.tmp/%'",
), __LINE__, __FILE__, false, 'GROUP BY share_path '.$having) as $row) ), __LINE__, __FILE__, false, 'GROUP BY share_path '.$having) as $row)
{ {
egw_vfs::remove($row['share_path']); Vfs::remove($row['share_path']);
if ($group_concat) if ($group_concat)
{ {
@ -629,10 +643,10 @@ class egw_sharing
} }
} }
} }
catch (Exception $e) { catch (\Exception $e) {
unset($e); unset($e);
} }
egw_vfs::$is_root = false; Vfs::$is_root = false;
} }
/** /**
@ -661,7 +675,7 @@ if (file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php'))
{ {
require_once __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 class SharingUi extends filemanager_ui
{ {
/** /**
* Get the configured start directory for the current user * Get the configured start directory for the current user

View File

@ -7,24 +7,30 @@
* @link http://www.egroupware.org * @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api * @package api
* @subpackage vfs * @subpackage webdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Hartmut Holzgraefe <hartmut@php.net> original HTTP/WebDAV/Server/Filesystem class, of which some code is used * @author Hartmut Holzgraefe <hartmut@php.net> original HTTP/WebDAV/Server/Filesystem class, of which some code is used
* @version $Id$ * @version $Id$
*/ */
if (strpos(ini_get('include_path'), EGW_API_INC) === false) namespace EGroupware\Api\Vfs;
{
ini_set('include_path', EGW_API_INC.PATH_SEPARATOR.ini_get('include_path')); require_once dirname(__DIR__).'/WebDAV/Server/Filesystem.php';
}
require_once('HTTP/WebDAV/Server/Filesystem.php'); use HTTP_WebDAV_Server_Filesystem;
use HTTP_WebDAV_Server;
use EGroupware\Api\Vfs;
use EGroupware\Api;
// old, not yet ported api classes
use common; // egw_exit
/** /**
* FileManger - WebDAV access using the new stream wrapper VFS interface * FileManger - WebDAV access using the new stream wrapper VFS interface
* *
* Using modified PEAR HTTP/WebDAV/Server/Filesystem class in API dir * Using modified PEAR HTTP/WebDAV/Server/Filesystem class in API dir
*/ */
class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem class WebDAV extends HTTP_WebDAV_Server_Filesystem
{ {
/** /**
* Realm of eGW's WebDAV server * Realm of eGW's WebDAV server
@ -39,7 +45,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
* *
* @var string * @var string
*/ */
var $base = egw_vfs::PREFIX; var $base = Vfs::PREFIX;
/** /**
* Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array * Debug level: 0 = nothing, 1 = function calls, 2 = more info, eg. complete $_SERVER array
@ -90,11 +96,11 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{ {
// recursive delete the directory // recursive delete the directory
try { try {
$deleted = egw_vfs::remove($options['path']); $deleted = Vfs::remove($options['path']);
$ret = !empty($deleted[$options['path']]); $ret = !empty($deleted[$options['path']]);
//error_log(__METHOD__."() egw_vfs::remove($options[path]) returned ".array2string($deleted)." --> ".array2string($ret)); //error_log(__METHOD__."() Vfs::remove($options[path]) returned ".array2string($deleted)." --> ".array2string($ret));
} }
catch (Exception $e) { catch (\Exception $e) {
return '403 Forbidden: '.$e->getMessage(); return '403 Forbidden: '.$e->getMessage();
} }
} }
@ -120,7 +126,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
function MKCOL($options) function MKCOL($options)
{ {
$path = $this->_unslashify($this->base .$options["path"]); $path = $this->_unslashify($this->base .$options["path"]);
$parent = egw_vfs::dirname($path); $parent = Vfs::dirname($path);
if (!file_exists($parent)) { if (!file_exists($parent)) {
return "409 Conflict"; return "409 Conflict";
@ -225,7 +231,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
} }
} else { } else {
if (is_dir($source) && $options['depth'] == 'infinity') { if (is_dir($source) && $options['depth'] == 'infinity') {
$files = egw_vfs::find($source,array('depth' => true,'url' => true)); // depth=true: return dirs first, url=true: allow urls! $files = Vfs::find($source,array('depth' => true,'url' => true)); // depth=true: return dirs first, url=true: allow urls!
} else { } else {
$files = array($source); $files = array($source);
} }
@ -289,41 +295,41 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
$info['props'] = array(); $info['props'] = array();
// no special beautified displayname here ... // no special beautified displayname here ...
$info['props'][] = HTTP_WebDAV_Server::mkprop ('displayname', egw_vfs::basename(self::_unslashify($info['path']))); $info['props'][] = self::mkprop ('displayname', Vfs::basename(self::_unslashify($info['path'])));
// creation and modification time // creation and modification time
$info['props'][] = HTTP_WebDAV_Server::mkprop ('creationdate', filectime($fspath)); $info['props'][] = self::mkprop ('creationdate', filectime($fspath));
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getlastmodified', filemtime($fspath)); $info['props'][] = self::mkprop ('getlastmodified', filemtime($fspath));
// Microsoft extensions: last access time and 'hidden' status // Microsoft extensions: last access time and 'hidden' status
$info["props"][] = HTTP_WebDAV_Server::mkprop("lastaccessed", fileatime($fspath)); $info["props"][] = self::mkprop("lastaccessed", fileatime($fspath));
$info["props"][] = HTTP_WebDAV_Server::mkprop("ishidden", egw_vfs::is_hidden($fspath)); $info["props"][] = self::mkprop("ishidden", Vfs::is_hidden($fspath));
// type and size (caller already made sure that path exists) // type and size (caller already made sure that path exists)
if (is_dir($fspath)) { if (is_dir($fspath)) {
// directory (WebDAV collection) // directory (WebDAV collection)
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', array( $info['props'][] = self::mkprop ('resourcetype', array(
HTTP_WebDAV_Server::mkprop('collection', ''))); self::mkprop('collection', '')));
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory'); $info['props'][] = self::mkprop ('getcontenttype', 'httpd/unix-directory');
} else { } else {
// plain file (WebDAV resource) // plain file (WebDAV resource)
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', ''); $info['props'][] = self::mkprop ('resourcetype', '');
if (egw_vfs::is_readable($path)) { if (Vfs::is_readable($path)) {
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', egw_vfs::mime_content_type($path)); $info['props'][] = self::mkprop ('getcontenttype', Vfs::mime_content_type($path));
} else { } else {
error_log(__METHOD__."($path) $fspath is not readable!"); error_log(__METHOD__."($path) $fspath is not readable!");
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'application/x-non-readable'); $info['props'][] = self::mkprop ('getcontenttype', 'application/x-non-readable');
} }
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontentlength', filesize($fspath)); $info['props'][] = self::mkprop ('getcontentlength', filesize($fspath));
} }
// generate etag from inode (sqlfs: fs_id), modification time and size // generate etag from inode (sqlfs: fs_id), modification time and size
$stat = stat($fspath); $stat = stat($fspath);
$info['props'][] = HTTP_WebDAV_Server::mkprop('getetag', '"'.$stat['ino'].':'.$stat['mtime'].':'.$stat['size'].'"'); $info['props'][] = self::mkprop('getetag', '"'.$stat['ino'].':'.$stat['mtime'].':'.$stat['size'].'"');
/* returning the supportedlock property causes Windows DAV provider and Konqueror to not longer work /* returning the supportedlock property causes Windows DAV provider and Konqueror to not longer work
ToDo: return it only if explicitly requested ($options['props']) ToDo: return it only if explicitly requested ($options['props'])
// supportedlock property // supportedlock property
$info['props'][] = HTTP_WebDAV_Server::mkprop('supportedlock',' $info['props'][] = self::mkprop('supportedlock','
<D:lockentry> <D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope> <D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:lockscope> <D:locktype><D:write/></D:lockscope>
@ -384,13 +390,13 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
$_path = $info['path']; $_path = $info['path'];
if (!$n && $info['path'] != '/' && substr($info['path'],-1) == '/') $_path = substr($info['path'],0,-1); if (!$n && $info['path'] != '/' && substr($info['path'],-1) == '/') $_path = substr($info['path'],0,-1);
// need to encode path again, as $info['path'] is NOT encoded, but egw_vfs::(stat|propfind) require it // need to encode path again, as $info['path'] is NOT encoded, but Vfs::(stat|propfind) require it
// otherwise pathes containing url special chars like ? or # will not stat // otherwise pathes containing url special chars like ? or # will not stat
$path = egw_vfs::encodePath($_path); $path = Vfs::encodePath($_path);
$path2n[$path] = $n; $path2n[$path] = $n;
// adding some properties used instead of regular DAV times // adding some properties used instead of regular DAV times
if (($stat = egw_vfs::stat($path))) if (($stat = Vfs::stat($path)))
{ {
$fileprops =& $files['files'][$path2n[$path]]['props']; $fileprops =& $files['files'][$path2n[$path]]['props'];
foreach(self::$auto_props as $attr => $props) foreach(self::$auto_props as $attr => $props)
@ -414,14 +420,14 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
} }
} }
} }
if ($path2n && ($path2props = egw_vfs::propfind(array_keys($path2n),null))) if ($path2n && ($path2props = Vfs::propfind(array_keys($path2n),null)))
{ {
foreach($path2props as $path => $props) foreach($path2props as $path => $props)
{ {
$fileprops =& $files['files'][$path2n[$path]]['props']; $fileprops =& $files['files'][$path2n[$path]]['props'];
foreach($props as $prop) foreach($props as $prop)
{ {
if ($prop['ns'] == egw_vfs::DEFAULT_PROP_NAMESPACE && $prop['name'][0] == '#') // eGW's customfields if ($prop['ns'] == Vfs::DEFAULT_PROP_NAMESPACE && $prop['name'][0] == '#') // eGW's customfields
{ {
$prop['ns'] .= 'customfields/'; $prop['ns'] .= 'customfields/';
$prop['name'] = substr($prop['name'],1); $prop['name'] = substr($prop['name'],1);
@ -437,13 +443,13 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
/** /**
* Used eg. by get * Used eg. by get
* *
* @todo replace all calls to _mimetype with egw_vfs::mime_content_type() * @todo replace all calls to _mimetype with Vfs::mime_content_type()
* @param string $path * @param string $path
* @return string * @return string
*/ */
function _mimetype($path) function _mimetype($path)
{ {
return egw_vfs::mime_content_type($path); return Vfs::mime_content_type($path);
} }
/** /**
@ -454,7 +460,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
*/ */
function _is_readable($fspath) function _is_readable($fspath)
{ {
return egw_vfs::is_readable($fspath); return Vfs::is_readable($fspath);
} }
/** /**
@ -465,7 +471,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
*/ */
function _is_writable($fspath) function _is_writable($fspath)
{ {
return egw_vfs::is_writable($fspath); return Vfs::is_writable($fspath);
} }
/** /**
@ -480,7 +486,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
*/ */
function PROPPATCH(&$options) function PROPPATCH(&$options)
{ {
$path = translation::convert($options['path'],'utf-8'); $path = Api\Translation::convert($options['path'],'utf-8');
foreach ($options['props'] as $key => $prop) { foreach ($options['props'] as $key => $prop) {
$attributes = array(); $attributes = array();
@ -492,14 +498,14 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{ {
case 'srt_modifiedtime': case 'srt_modifiedtime':
case 'getlastmodified': case 'getlastmodified':
egw_vfs::touch($path,strtotime($prop['val'])); Vfs::touch($path,strtotime($prop['val']));
break; break;
//case 'srt_creationtime': //case 'srt_creationtime':
// no streamwrapper interface / php function to set the ctime currently // no streamwrapper interface / php function to set the ctime currently
//$attributes['created'] = strtotime($prop['val']); //$attributes['created'] = strtotime($prop['val']);
//break; //break;
default: default:
if (!egw_vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden'; if (!Vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden';
break; break;
} }
break; break;
@ -509,7 +515,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{ {
// allow netdrive to change the modification time // allow netdrive to change the modification time
case 'getlastmodified': case 'getlastmodified':
egw_vfs::touch($path,strtotime($prop['val'])); Vfs::touch($path,strtotime($prop['val']));
break; break;
// not sure why, the filesystem example of the WebDAV class does it ... // not sure why, the filesystem example of the WebDAV class does it ...
default: default:
@ -522,23 +528,23 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
switch($prop['name']) switch($prop['name'])
{ {
case 'Win32LastModifiedTime': case 'Win32LastModifiedTime':
egw_vfs::touch($path,strtotime($prop['val'])); Vfs::touch($path,strtotime($prop['val']));
break; break;
case 'Win32CreationTime': // eg. "Wed, 14 Sep 2011 15:48:26 GMT" case 'Win32CreationTime': // eg. "Wed, 14 Sep 2011 15:48:26 GMT"
case 'Win32LastAccessTime': case 'Win32LastAccessTime':
case 'Win32FileAttributes': // not sure what that is, it was always "00000000" case 'Win32FileAttributes': // not sure what that is, it was always "00000000"
default: default:
if (!egw_vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden'; if (!Vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden';
break; break;
} }
break; break;
case egw_vfs::DEFAULT_PROP_NAMESPACE.'customfields/': // eGW's customfields case Vfs::DEFAULT_PROP_NAMESPACE.'customfields/': // eGW's customfields
$prop['ns'] = egw_vfs::DEFAULT_PROP_NAMESPACE; $prop['ns'] = Vfs::DEFAULT_PROP_NAMESPACE;
$prop['name'] = '#'.$prop['name']; $prop['name'] = '#'.$prop['name'];
// fall through // fall through
default: default:
if (!egw_vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden'; if (!Vfs::proppatch($path,array($prop))) $options['props'][$key]['status'] = '403 Forbidden';
break; break;
} }
if ($this->debug) $props[] = '('.$prop['ns'].')'.$prop['name'].'='.$prop['val']; if ($this->debug) $props[] = '('.$prop['ns'].')'.$prop['name'].'='.$prop['val'];
@ -570,7 +576,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
// dont know why, but HTTP_WebDAV_Server passes the owner in D:href tags, which get's passed unchanged to checkLock/PROPFIND // dont know why, but HTTP_WebDAV_Server passes the owner in D:href tags, which get's passed unchanged to checkLock/PROPFIND
// that's wrong according to the standard and cadaver does not show it on discover --> strip_tags removes eventual tags // that's wrong according to the standard and cadaver does not show it on discover --> strip_tags removes eventual tags
if (($ret = egw_vfs::lock($options['path'],$options['locktoken'],$options['timeout'],strip_tags($options['owner']), if (($ret = Vfs::lock($options['path'],$options['locktoken'],$options['timeout'],strip_tags($options['owner']),
$options['scope'],$options['type'],isset($options['update']))) && !isset($options['update'])) $options['scope'],$options['type'],isset($options['update']))) && !isset($options['update']))
{ {
return $ret ? '200 OK' : '409 Conflict'; return $ret ? '200 OK' : '409 Conflict';
@ -587,7 +593,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
function UNLOCK(&$options) function UNLOCK(&$options)
{ {
if ($this->debug) error_log(__METHOD__.'('.str_replace(array("\n",' '),'',print_r($options,true)).')'); if ($this->debug) error_log(__METHOD__.'('.str_replace(array("\n",' '),'',print_r($options,true)).')');
return egw_vfs::unlock($options['path'],$options['token']) ? '204 No Content' : '409 Conflict'; return Vfs::unlock($options['path'],$options['token']) ? '204 No Content' : '409 Conflict';
} }
/** /**
@ -598,7 +604,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
*/ */
function checkLock($path) function checkLock($path)
{ {
return egw_vfs::checkLock($path); return Vfs::checkLock($path);
} }
/** /**
@ -612,7 +618,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
function GetDir($fspath, &$options) function GetDir($fspath, &$options)
{ {
// add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv) // add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv)
header('Content-type: text/html; charset='.translation::charset()); header('Content-type: text/html; charset='.Api\Translation::charset());
parent::GetDir($fspath, $options); parent::GetDir($fspath, $options);
} }
@ -650,7 +656,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
if (($ok = parent::GET($options))) if (($ok = parent::GET($options)))
{ {
// mitigate risks of serving javascript or css from our domain // mitigate risks of serving javascript or css from our domain
html::safe_content_header($options['stream'], $options['path'], $options['mimetype'], $options['size'], false, Api\Header\Content::safe($options['stream'], $options['path'], $options['mimetype'], $options['size'], false,
$this->force_download, true); // true = do not send content-type and content-length header, but modify values $this->force_download, true); // true = do not send content-type and content-length header, but modify values
if (!is_resource($options['stream'])) if (!is_resource($options['stream']))
@ -678,7 +684,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{ {
return $ret; // no collection return $ret; // no collection
} }
header('Content-type: text/html; charset='.translation::charset()); header('Content-type: text/html; charset='.Api\Translation::charset());
echo "<html>\n<head>\n\t<title>".'EGroupware WebDAV server '.htmlspecialchars($options['path'])."</title>\n"; echo "<html>\n<head>\n\t<title>".'EGroupware WebDAV server '.htmlspecialchars($options['path'])."</title>\n";
echo "\t<meta http-equiv='content-type' content='text/html; charset=utf-8' />\n"; echo "\t<meta http-equiv='content-type' content='text/html; charset=utf-8' />\n";
echo "\t<style type='text/css'>\n.th { background-color: #e0e0e0; }\n.row_on { background-color: #F1F1F1; vertical-align: top; }\n". echo "\t<style type='text/css'>\n.th { background-color: #e0e0e0; }\n.row_on { background-color: #F1F1F1; vertical-align: top; }\n".
@ -691,7 +697,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
foreach(explode('/',$this->_unslashify($options['path'])) as $n => $name) foreach(explode('/',$this->_unslashify($options['path'])) as $n => $name)
{ {
$path .= ($n != 1 ? '/' : '').$name; $path .= ($n != 1 ? '/' : '').$name;
echo html::a_href(htmlspecialchars($name.'/'),$path); echo Api\Html::a_href(htmlspecialchars($name.'/'),$path);
} }
echo "</h1>\n"; echo "</h1>\n";
@ -739,7 +745,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
} }
echo "\t<tr class='$class'>\n\t\t<td>$n</td>\n\t\t<td>". echo "\t<tr class='$class'>\n\t\t<td>$n</td>\n\t\t<td>".
html::a_href(htmlspecialchars($name),$base.strtr($file['path'], array( Api\Html::a_href(htmlspecialchars($name),$base.strtr($file['path'], array(
'%' => '%25', '%' => '%25',
'#' => '%23', '#' => '%23',
'?' => '%3F', '?' => '%3F',

View File

@ -33,9 +33,9 @@
+----------------------------------------------------------------------+ +----------------------------------------------------------------------+
*/ */
require_once "HTTP/WebDAV/Tools/_parse_propfind.php"; require_once __DIR__."/Tools/_parse_propfind.php";
require_once "HTTP/WebDAV/Tools/_parse_proppatch.php"; require_once __DIR__."/Tools/_parse_proppatch.php";
require_once "HTTP/WebDAV/Tools/_parse_lockinfo.php"; require_once __DIR__."/Tools/_parse_lockinfo.php";
/** /**
* Virtual base class for implementing WebDAV servers * Virtual base class for implementing WebDAV servers

View File

@ -33,8 +33,7 @@
+----------------------------------------------------------------------+ +----------------------------------------------------------------------+
*/ */
require_once "HTTP/WebDAV/Server.php"; require_once dirname(__DIR__).'/Server.php';
require_once "System.php";
/** /**
* Filesystem access using WebDAV * Filesystem access using WebDAV

View File

@ -5,11 +5,14 @@
* @link http://www.egroupware.org/ * @link http://www.egroupware.org/
* @package filemanager * @package filemanager
* @author Ralf Becker <rb-AT-stylite.de> * @author Ralf Becker <rb-AT-stylite.de>
* @copyright (c) 2014 by Ralf Becker <rb-AT-stylite.de> * @copyright (c) 2014-16 by Ralf Becker <rb-AT-stylite.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$ * @version $Id$
*/ */
use EGroupware\Api;
use EGroupware\Api\Vfs\Sharing;
/** /**
* Filemanager: shares * Filemanager: shares
*/ */
@ -40,7 +43,7 @@ class filemanager_shares extends filemanager_ui
{ {
// sudo handling // sudo handling
parent::__construct(); parent::__construct();
self::$is_setup = egw_session::appsession('is_setup','filemanager'); self::$is_setup = Api\Cache::getSession('filemanager', 'is_setup');
self::$tmp_dir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp/'; self::$tmp_dir = '/home/'.$GLOBALS['egw_info']['user']['account_lid'].'/.tmp/';
} }
@ -57,16 +60,16 @@ class filemanager_shares extends filemanager_ui
{ {
switch ($query['col_filter']['type']) switch ($query['col_filter']['type'])
{ {
case egw_sharing::LINK: case Sharing::LINK:
$query['col_filter'][] = "share_path LIKE ".$GLOBALS['egw']->db->quote(self::$tmp_dir.'%'); $query['col_filter'][] = "share_path LIKE ".$GLOBALS['egw']->db->quote(self::$tmp_dir.'%');
break; break;
case egw_sharing::READONLY: case Sharing::READONLY:
$query['col_filter'][] = "share_path NOT LIKE ".$GLOBALS['egw']->db->quote(self::$tmp_dir.'%'); $query['col_filter'][] = "share_path NOT LIKE ".$GLOBALS['egw']->db->quote(self::$tmp_dir.'%');
$query['col_filter']['share_writable'] = false; $query['col_filter']['share_writable'] = false;
break; break;
case egw_sharing::WRITABLE: case Sharing::WRITABLE:
$query['col_filter']['share_writable'] = true; $query['col_filter']['share_writable'] = true;
break; break;
} }
@ -82,18 +85,18 @@ class filemanager_shares extends filemanager_ui
$query['col_filter']['share_owner'] = $GLOBALS['egw_info']['user']['account_id']; $query['col_filter']['share_owner'] = $GLOBALS['egw_info']['user']['account_id'];
$readonlys = null; $readonlys = null;
$total = egw_sharing::so()->get_rows($query, $rows, $readonlys); $total = Sharing::so()->get_rows($query, $rows, $readonlys);
foreach($rows as &$row) foreach($rows as &$row)
{ {
if (substr($row['share_path'], 0, strlen(self::$tmp_dir)) === self::$tmp_dir) if (substr($row['share_path'], 0, strlen(self::$tmp_dir)) === self::$tmp_dir)
{ {
$row['share_path'] = substr($row['share_path'], strlen(self::$tmp_dir)); $row['share_path'] = substr($row['share_path'], strlen(self::$tmp_dir));
$row['type'] = egw_sharing::LINK; $row['type'] = Sharing::LINK;
} }
else else
{ {
$row['type'] = $row['share_writable'] ? egw_sharing::WRITABLE : egw_sharing::READONLY; $row['type'] = $row['share_writable'] ? Sharing::WRITABLE : Sharing::READONLY;
} }
$row['share_passwd'] = (boolean)$row['share_passwd']; $row['share_passwd'] = (boolean)$row['share_passwd'];
if ($row['share_with']) $row['share_with'] = preg_replace('/,([^ ])/', ', $1', $row['share_with']); if ($row['share_with']) $row['share_with'] = preg_replace('/,([^ ])/', ', $1', $row['share_with']);
@ -159,25 +162,25 @@ class filemanager_shares extends filemanager_ui
{ {
$where['share_id'] = $content['nm']['selected']; $where['share_id'] = $content['nm']['selected'];
} }
egw_framework::message(lang('%1 shares deleted.', egw_sharing::delete($where)), 'success'); egw_framework::message(lang('%1 shares deleted.', Sharing::delete($where)), 'success');
break; break;
default: default:
throw new egw_exception_wrong_parameter("Unknown action '{$content['nm']['action']}'!"); throw new Api\Exception\WrongParameter("Unknown action '{$content['nm']['action']}'!");
} }
unset($content['nm']['action']); unset($content['nm']['action']);
} }
$content['is_setup'] = self::$is_setup; $content['is_setup'] = self::$is_setup;
$sel_options = array( $sel_options = array(
'type' => egw_sharing::$modes, 'type' => Sharing::$modes,
'share_passwd' => array( 'share_passwd' => array(
'no' => lang('No'), 'no' => lang('No'),
'yes' => lang('Yes'), 'yes' => lang('Yes'),
) )
); );
unset($sel_options['type'][egw_sharing::ATTACH]); unset($sel_options['type'][Sharing::ATTACH]);
$tpl = new etemplate_new('filemanager.shares'); $tpl = new Api\Etemplate('filemanager.shares');
$tpl->exec('filemanager.filemanager_shares.index', $content, $sel_options, null, $content); $tpl->exec('filemanager.filemanager_shares.index', $content, $sel_options, null, $content);
} }
} }

View File

@ -13,10 +13,13 @@
* @package api * @package api
* @subpackage vfs * @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2006-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2006-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
use EGroupware\Api;
use EGroupware\Vfs;
$starttime = microtime(true); $starttime = microtime(true);
/** /**
@ -33,7 +36,7 @@ function check_access(&$account)
{ {
list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2); list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2);
} }
return egw_digest_auth::autocreate_session_callback($account); return Api\Header\Authenticate::autocreate_session_callback($account);
} }
$GLOBALS['egw_info'] = array( $GLOBALS['egw_info'] = array(
@ -43,17 +46,16 @@ $GLOBALS['egw_info'] = array(
'currentapp' => preg_match('|/webdav.php/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager', 'currentapp' => preg_match('|/webdav.php/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager',
'autocreate_session_callback' => 'check_access', 'autocreate_session_callback' => 'check_access',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
'auth_realm' => 'EGroupware WebDAV server', // cant use vfs_webdav_server::REALM as autoloading and include path not yet setup! 'auth_realm' => 'EGroupware WebDAV server', // cant use Vfs\WebDAV::REALM as autoloading and include path not yet setup!
) )
); );
require_once('../phpgwapi/inc/class.egw_digest_auth.inc.php');
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
try try
{ {
include(dirname(__DIR__).'/header.inc.php'); include(dirname(__DIR__).'/header.inc.php');
} }
catch (egw_exception_no_permission_app $e) catch (Api\Exception\NoPermission\App $e)
{ {
if (isset($GLOBALS['egw_info']['user']['apps']['filemanager'])) if (isset($GLOBALS['egw_info']['user']['apps']['filemanager']))
{ {
@ -73,18 +75,18 @@ catch (egw_exception_no_permission_app $e)
// temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted // temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted
// so ownCloud dir contains users home-dir by default // so ownCloud dir contains users home-dir by default
if (strpos($_SERVER['REQUEST_URI'],'/webdav.php/clientsync') !== false && if (strpos($_SERVER['REQUEST_URI'],'/webdav.php/clientsync') !== false &&
($fstab=egw_vfs::mount()) && !isset($fstab['/clientsync'])) ($fstab=Vfs::mount()) && !isset($fstab['/clientsync']))
{ {
$is_root_backup = egw_vfs::$is_root; $is_root_backup = Vfs::$is_root;
egw_vfs::$is_root = true; Vfs::$is_root = true;
$ok = egw_vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false); $ok = Vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
egw_vfs::$is_root = $is_root_backup; Vfs::$is_root = $is_root_backup;
//error_log("mounting ownCloud default '$clientsync' as '$url' ".($ok ? 'successful' : 'failed!')); //error_log("mounting ownCloud default '$clientsync' as '$url' ".($ok ? 'successful' : 'failed!'));
} }
// webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session // webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session
$GLOBALS['egw']->session->commit_session(); $GLOBALS['egw']->session->commit_session();
$webdav_server = new vfs_webdav_server(); $webdav_server = new Vfs\WebDAV();
$webdav_server->ServeRequest(); $webdav_server->ServeRequest();
//error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime)); //error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime));

View File

@ -16,7 +16,7 @@
* @package api * @package api
* @subpackage groupdav * @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
@ -36,13 +36,12 @@ $GLOBALS['egw_info'] = array(
'noheader' => True, 'noheader' => True,
'currentapp' => 'groupdav', 'currentapp' => 'groupdav',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
'autocreate_session_callback' => array('egw_digest_auth','autocreate_session_callback'), 'autocreate_session_callback' => 'EGroupware\\Api\\Header\\Authenticate::autocreate_session_callback',
'auth_realm' => 'EGroupware CalDAV/CardDAV/GroupDAV server', // cant use groupdav::REALM as autoloading and include path not yet setup! 'auth_realm' => 'EGroupware CalDAV/CardDAV/GroupDAV server', // cant use groupdav::REALM as autoloading and include path not yet setup!
) )
); );
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
$egw_dir = dirname(__FILE__); $egw_dir = dirname(__FILE__);
require_once($egw_dir.'/phpgwapi/inc/class.egw_digest_auth.inc.php');
include($egw_dir.'/header.inc.php'); include($egw_dir.'/header.inc.php');
$GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository();

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* eGroupWare API: Basic and Digest Auth * EGroupware API: Basic and Digest Auth
* *
* For Apache FCGI you need the following rewrite rule: * For Apache FCGI you need the following rewrite rule:
* *
@ -15,256 +15,15 @@
* @package api * @package api
* @subpackage auth * @subpackage auth
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2010 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2010-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
use EGroupware\Api\Header\Authenticate;
/** /**
* Class to authenticate via basic or digest auth * Class to authenticate via basic or digest auth
* *
* The more secure digest auth requires: * @deprecated use EGroupware\Api\Header\Authenticate
* a) cleartext passwords in SQL table
* b) md5 hashes of username, realm, password stored somewhere (NOT yet implemented)
* Otherwise digest auth is not possible and therefore not offered to the client.
*
* Usage example:
*
* $GLOBALS['egw_info']['flags'] = array(
* 'noheader' => True,
* 'currentapp' => 'someapp',
* 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
* 'autocreate_session_callback' => array('egw_digest_auth','autocreate_session_callback'),
* 'auth_realm' => 'EGroupware',
* );
* include(dirname(__FILE__).'/header.inc.php');
*
* @link http://www.php.net/manual/en/features.http-auth.php
* @ToDo check if we have to check if returned nonce matches our challange (not done in above link, but why would it be there)
* @link http://en.wikipedia.org/wiki/Digest_access_authentication
* @link http://tools.ietf.org/html/rfc2617
*
* Commented out is accept-charset parameter from (seems not supported by any client I tested with)
* @link https://tools.ietf.org/id/draft-reschke-basicauth-enc-06.html
*
* Implemented support for clients sending credentials in in iso-8859-1 instead of our utf-8:
* - Firefox 19.0
* - Thunderbird 17.0.3 with Lightning 1.8
* - IE 8
* - Netdrive
* (Chrome 24 or Safari 6 sends credentials in charset of webpage.)
*/ */
class egw_digest_auth class egw_digest_auth extends Authenticate {}
{
/**
* Log to error_log:
* 0 = dont
* 1 = no cleartext passwords
* 2 = all
*/
const ERROR_LOG = 0;
/**
* Callback to be used to create session via header include authenticated via basic or digest auth
*
* @param array $account NOT used!
* @return string valid session-id or does NOT return at all!
*/
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'] : '**********';
error_log(__METHOD__.'() PHP_AUTH_USER='.array2string($_SERVER['PHP_AUTH_USER']).', PHP_AUTH_PW='.array2string($pw).', PHP_AUTH_DIGEST='.array2string($_SERVER['PHP_AUTH_DIGEST']));
}
$realm = $GLOBALS['egw_info']['flags']['auth_realm'];
if (empty($realm)) $realm = 'EGroupware';
$username = $_SERVER['PHP_AUTH_USER']; $password = $_SERVER['PHP_AUTH_PW'];
// Support for basic auth when using PHP CGI (what about digest auth?)
if (!isset($username) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'],'Basic ') === 0)
{
$hash = base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'],6));
if (strpos($hash, ':') !== false)
{
list($username, $password) = explode(':', $hash, 2);
}
}
elseif (isset($_SERVER['PHP_AUTH_DIGEST']) && !self::is_valid($realm,$_SERVER['PHP_AUTH_DIGEST'],$username,$password))
{
unset($password);
}
// if given password contains non-ascii chars AND we can not authenticate with it
if (isset($username) && isset($password) &&
(preg_match('/[^\x20-\x7F]/', $password) || strpos($password, '\\x') !== false) &&
!$GLOBALS['egw']->auth->authenticate($username, $password, 'text'))
{
self::decode_password($password);
}
// 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;
header('WWW-Authenticate: Basic realm="'.$realm.'"');// draft-reschke-basicauth-enc-06 adds, accept-charset="'.translation::charset().'"');
self::digest_header($realm);
header('HTTP/1.1 401 Unauthorized');
header('X-WebDAV-Status: 401 Unauthorized', true);
echo "<html>\n<head>\n<title>401 Unauthorized</title>\n<body>\nAuthorization failed.\n</body>\n</html>\n";
exit;
}
return $sessionid;
}
/**
* Decode password containing non-ascii chars
*
* @param string &$password
* @return boolean true if conversation happend, false if there was no need for a conversation
*/
public static function decode_password(&$password)
{
// if given password contains non-ascii chars AND we can not authenticate with it
if (preg_match('/[^\x20-\x7F]/', $password) || strpos($password, '\\x') !== false)
{
// replace \x encoded non-ascii chars in password, as they are used eg. by Thunderbird for German umlauts
if (strpos($password, '\\x') !== false)
{
$password = preg_replace_callback('/\\\\x([0-9A-F]{2})/i', function($matches){
return chr(hexdec($matches[1]));
}, $password);
}
// try translating the password from iso-8859-1 to utf-8
$password = translation::convert($password, 'iso-8859-1');
//error_log(__METHOD__."() Fixed non-ascii password of user '$username' from '$_SERVER[PHP_AUTH_PW]' to '$password'");
return true;
}
return false;
}
/**
* Check if digest auth is available for a given realm (and user): do we use cleartext passwords
*
* 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
* @return boolean true if digest auth is available, false otherwise
*/
static public function digest_auth_available($realm,$username=null,&$user_pw=null)
{
// we currently require plaintext passwords!
if (!($GLOBALS['egw_info']['server']['auth_type'] == 'sql' && $GLOBALS['egw_info']['server']['sql_encryption_type'] == 'plain') ||
$GLOBALS['egw_info']['server']['auth_type'] == 'ldap' && $GLOBALS['egw_info']['server']['ldap_encryption_type'] == 'plain')
{
if (self::ERROR_LOG) error_log(__METHOD__."('$username') return false (no plaintext passwords used)");
return false; // no plain-text passwords used
}
// check for specific user, if given
if (!is_null($username) && !(($user_pw = $GLOBALS['egw']->accounts->id2name($username,'account_pwd','u')) ||
$GLOBALS['egw_info']['server']['auth_type'] == 'sql' && substr($user_pw,0,7) != '{PLAIN}'))
{
unset($user_pw);
if (self::ERROR_LOG) error_log(__METHOD__."('$realm','$username') return false (unknown user or NO plaintext password for user)");
return false; // user does NOT exist, or has no plaintext passwords (ldap server requires real root_dn or special ACL!)
}
if (substr($user_pw,0,7) == '{PLAIN}') $user_pw = substr($user_pw,7);
if (self::ERROR_LOG)
{
$pw = self::ERROR_LOG > 1 ? $user_pw : '**********';
error_log(__METHOD__."('$realm','$username','$pw') return true");
}
return true;
}
/**
* Send header offering digest auth, if it's generally available
*
* @param string $realm
* @param string &$nonce=null on return
*/
static public function digest_header($realm,&$nonce=null)
{
if (self::digest_auth_available($realm))
{
$nonce = uniqid();
header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.$nonce.'",opaque="'.md5($realm).'"');
if (self::ERROR_LOG) error_log(__METHOD__."() offering digest auth for realm '$realm' using nonce='$nonce'");
}
}
/**
* Check digest
*
* @param string $realm
* @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
*/
static public function is_valid($realm,$auth_digest=null,&$username=null,&$password=null)
{
if (is_null($auth_digest)) $auth_digest = $_SERVER['PHP_AUTH_DIGEST'];
$data = self::parse_digest($auth_digest);
if (!$data || !($A1 = self::get_digest_A1($realm,$username=$data['username'],$password=null)))
{
error_log(__METHOD__."('$realm','$auth_digest','$username') returning FALSE");
return false;
}
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if (self::ERROR_LOG) error_log(__METHOD__."('$realm','$auth_digest','$username') response='$data[response]', valid_response='$valid_response' returning ".array2string($data['response'] === $valid_response));
return $data['response'] === $valid_response;
}
/**
* Calculate the A1 digest hash
*
* @param string $realm
* @param string $username
* @param string &$password=null password to use or if null, on return stored password
* @return string|boolean false if $password not given and can NOT be read
*/
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;
}
if (is_null($password)) $password = $user_pw;
$A1 = md5($username . ':' . $realm . ':' . $password);
if (self::ERROR_LOG > 1) error_log(__METHOD__."('$realm','$username','$password') returning ".array2string($A1));
return $A1;
}
/**
* Parse the http auth header
*/
static public function parse_digest($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$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)
{
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
//error_log(__METHOD__."('$txt') returning ".array2string($needed_parts ? false : $data));
return $needed_parts ? false : $data;
}
}

View File

@ -7,15 +7,11 @@
* @package api * @package api
* @subpackage groupdav * @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-14 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
if (strpos(ini_get('include_path'), EGW_API_INC) === false) require_once(EGW_INCLUDE_ROOT.'/api/src/WebDAV/Server.php');
{
ini_set('include_path', EGW_API_INC.PATH_SEPARATOR.ini_get('include_path'));
}
require_once('HTTP/WebDAV/Server.php');
/** /**
* EGroupware: GroupDAV access * EGroupware: GroupDAV access

View File

@ -13,10 +13,13 @@
* @package api * @package api
* @subpackage vfs * @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2006-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2006-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
use EGroupware\Api;
use EGroupware\Vfs;
$starttime = microtime(true); $starttime = microtime(true);
/** /**
@ -33,7 +36,7 @@ function check_access(&$account)
{ {
list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2); list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2);
} }
return egw_digest_auth::autocreate_session_callback($account); return Api\Header\Authenticate::autocreate_session_callback($account);
} }
$GLOBALS['egw_info'] = array( $GLOBALS['egw_info'] = array(
@ -43,17 +46,16 @@ $GLOBALS['egw_info'] = array(
'currentapp' => preg_match('|/remote.php/webdav/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager', 'currentapp' => preg_match('|/remote.php/webdav/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager',
'autocreate_session_callback' => 'check_access', 'autocreate_session_callback' => 'check_access',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
'auth_realm' => 'EGroupware WebDAV server', // cant use vfs_webdav_server::REALM as autoloading and include path not yet setup! 'auth_realm' => 'EGroupware WebDAV server', // cant use Vfs\WebDAV::REALM as autoloading and include path not yet setup!
) )
); );
require_once(__DIR__.'/phpgwapi/inc/class.egw_digest_auth.inc.php');
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
try try
{ {
include(__DIR__.'/header.inc.php'); include(__DIR__.'/header.inc.php');
} }
catch (egw_exception_no_permission_app $e) catch (Api\Exception\NoPermission\App $e)
{ {
if (isset($GLOBALS['egw_info']['user']['apps']['filemanager'])) if (isset($GLOBALS['egw_info']['user']['apps']['filemanager']))
{ {
@ -73,18 +75,18 @@ catch (egw_exception_no_permission_app $e)
// temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted // temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted
// so ownCloud dir contains users home-dir by default // so ownCloud dir contains users home-dir by default
if (strpos($_SERVER['REQUEST_URI'],'/remote.php/webdav/clientsync') !== false && if (strpos($_SERVER['REQUEST_URI'],'/remote.php/webdav/clientsync') !== false &&
($fstab=egw_vfs::mount()) && !isset($fstab['/clientsync'])) ($fstab=Vfs::mount()) && !isset($fstab['/clientsync']))
{ {
$is_root_backup = egw_vfs::$is_root; $is_root_backup = Vfs::$is_root;
egw_vfs::$is_root = true; Vfs::$is_root = true;
$ok = egw_vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false); $ok = Vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
egw_vfs::$is_root = $is_root_backup; Vfs::$is_root = $is_root_backup;
//error_log("mounting ownCloud default '$clientsync' as '$url' ".($ok ? 'successful' : 'failed!')); //error_log("mounting ownCloud default '$clientsync' as '$url' ".($ok ? 'successful' : 'failed!'));
} }
// webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session // webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session
$GLOBALS['egw']->session->commit_session(); $GLOBALS['egw']->session->commit_session();
$webdav_server = new vfs_webdav_server(); $webdav_server = new Vfs\WebDAV();
$webdav_server->ServeRequest('/webdav'); $webdav_server->ServeRequest('/webdav');
//error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime)); //error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime));

View File

@ -6,11 +6,13 @@
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api * @package api
* @author Ralf Becker <rb@stylite.de> * @author Ralf Becker <rb@stylite.de>
* @copyright (c) 2014/15 by Ralf Becker <rb@stylite.de> * @copyright (c) 2014-16 by Ralf Becker <rb@stylite.de>
* @version $Id$ * @version $Id$
*/ */
require_once('./phpgwapi/inc/class.egw_sharing.inc.php'); require_once(__DIR__.'/api/src/Vfs/Sharing.php');
use EGroupware\Api\Vfs\Sharing;
$GLOBALS['egw_info'] = array( $GLOBALS['egw_info'] = array(
'flags' => array( 'flags' => array(
@ -18,7 +20,7 @@ $GLOBALS['egw_info'] = array(
'noheader' => true, 'noheader' => true,
'nonavbar' => 'always', // true would cause eTemplate to reset it to false for non-popups! 'nonavbar' => 'always', // true would cause eTemplate to reset it to false for non-popups!
'currentapp' => 'filemanager', 'currentapp' => 'filemanager',
'autocreate_session_callback' => 'egw_sharing::create_session', 'autocreate_session_callback' => 'EGroupware\\Api\\Vfs\\Sharing::create_session',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
) )
); );
@ -27,6 +29,6 @@ include('./header.inc.php');
if (!$GLOBALS['egw']->sharing) if (!$GLOBALS['egw']->sharing)
{ {
egw_sharing::create_session(true); // true = mount into existing session Sharing::create_session(true); // true = mount into existing session
} }
$GLOBALS['egw']->sharing->ServeRequest(); $GLOBALS['egw']->sharing->ServeRequest();

View File

@ -16,10 +16,13 @@
* @package api * @package api
* @subpackage vfs * @subpackage vfs
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2006-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2006-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$ * @version $Id$
*/ */
use EGroupware\Api;
use EGroupware\Api\Vfs;
$starttime = microtime(true); $starttime = microtime(true);
/** /**
@ -36,7 +39,7 @@ function check_access(&$account)
{ {
list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2); list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) = explode(':',base64_decode($_GET['auth']),2);
} }
return egw_digest_auth::autocreate_session_callback($account); return Api\Header\Authenticate::autocreate_session_callback($account);
} }
$GLOBALS['egw_info'] = array( $GLOBALS['egw_info'] = array(
@ -46,17 +49,16 @@ $GLOBALS['egw_info'] = array(
'currentapp' => preg_match('|/webdav.php/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager', 'currentapp' => preg_match('|/webdav.php/apps/([A-Za-z0-9_-]+)/|', $_SERVER['REQUEST_URI'], $matches) ? $matches[1] : 'filemanager',
'autocreate_session_callback' => 'check_access', 'autocreate_session_callback' => 'check_access',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm) 'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
'auth_realm' => 'EGroupware WebDAV server', // cant use vfs_webdav_server::REALM as autoloading and include path not yet setup! 'auth_realm' => 'EGroupware WebDAV server', // cant use Vfs\WebDAV::REALM as autoloading and include path not yet setup!
) )
); );
require_once('phpgwapi/inc/class.egw_digest_auth.inc.php');
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
try try
{ {
include(dirname(__FILE__).'/header.inc.php'); include(dirname(__FILE__).'/header.inc.php');
} }
catch (egw_exception_no_permission_app $e) catch (Api\Exception\NoPermission\App $e)
{ {
if (isset($GLOBALS['egw_info']['user']['apps']['filemanager'])) if (isset($GLOBALS['egw_info']['user']['apps']['filemanager']))
{ {
@ -76,6 +78,6 @@ catch (egw_exception_no_permission_app $e)
// webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session // webdav is stateless: we dont need to keep the session open, it only blocks other calls to same basic-auth session
$GLOBALS['egw']->session->commit_session(); $GLOBALS['egw']->session->commit_session();
$webdav_server = new vfs_webdav_server(); $webdav_server = new Vfs\WebDAV();
$webdav_server->ServeRequest(); $webdav_server->ServeRequest();
//error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime)); //error_log(sprintf('WebDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'],$webdav_server->_http_status,microtime(true)-$starttime,$headertime-$starttime));