moving egw_digest_auth, vfs_webdav_server and egw_sharing to new api
This commit is contained in:
parent
3692f01100
commit
67cb60b972
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -28,9 +28,9 @@ class Content
|
|||
*
|
||||
* Mitigate risk of html downloads by using CSP or force download for IE
|
||||
*
|
||||
* @param resource|string &$content content might be changed by this call
|
||||
* @param resource|string& $content content might be changed by this call
|
||||
* @param string $path filename or path for content-disposition header
|
||||
* @param string &$mime ='' mimetype or '' (default) to detect it from filename, using mime_magic::filename2mime()
|
||||
* @param string& $mime ='' mimetype or '' (default) to detect it from filename, using mime_magic::filename2mime()
|
||||
* on return used, maybe changed, mime-type
|
||||
* @param int $length =0 content length, default 0 = skip that header
|
||||
* on return changed size
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace EGroupware\Api;
|
|||
|
||||
// explicitly reference classes still in phpgwapi
|
||||
use egw_mailer;
|
||||
use egw_digest_auth; // egw_digest_auth::parse_digest
|
||||
|
||||
/**
|
||||
* 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
|
||||
// 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'].
|
||||
EGW_SERVER_ROOT.':'.self::getuser_ip().':'.filemtime(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php').
|
||||
':'.$_SERVER['HTTP_USER_AGENT']);
|
||||
|
|
|
@ -5,16 +5,30 @@
|
|||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage Vfs
|
||||
* @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$
|
||||
*/
|
||||
|
||||
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
|
||||
*
|
||||
* 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:
|
||||
* - 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 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)
|
||||
|
@ -184,9 +198,9 @@ class egw_sharing
|
|||
|
||||
// check password, if required
|
||||
if ($share['share_passwd'] && (empty($_SERVER['PHP_AUTH_PW']) ||
|
||||
!(auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt') ||
|
||||
egw_digest_auth::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') ||
|
||||
Api\Header\Authenticate::decode_password($_SERVER['PHP_AUTH_PW']) &&
|
||||
Api\Auth::compare_password($_SERVER['PHP_AUTH_PW'], $share['share_passwd'], 'crypt'))))
|
||||
{
|
||||
$realm = 'EGroupware share '.$share['share_token'];
|
||||
header('WWW-Authenticate: Basic realm="'.$realm.'"');
|
||||
|
@ -201,10 +215,10 @@ class egw_sharing
|
|||
if (count($GLOBALS['egw_info']['server']['vfs_fstab']) <= 1)
|
||||
{
|
||||
unset($GLOBALS['egw_info']['server']['vfs_fstab']); // triggers reset of fstab in mount()
|
||||
$GLOBALS['egw_info']['server']['vfs_fstab'] = egw_vfs::mount();
|
||||
egw_vfs::clearstatcache();
|
||||
$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
|
||||
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 (!self::$db->from_bool($share['share_writable']))
|
||||
{
|
||||
|
@ -217,7 +231,7 @@ class egw_sharing
|
|||
$share['share_root'] = '/'.$share['share_token'];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -230,12 +244,12 @@ class egw_sharing
|
|||
);
|
||||
|
||||
$share['share_root'] = '/';
|
||||
egw_vfs::$user = $share['share_owner'];
|
||||
Vfs::$user = $share['share_owner'];
|
||||
}
|
||||
|
||||
// mounting share
|
||||
egw_vfs::$is_root = true;
|
||||
if (!egw_vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session))
|
||||
Vfs::$is_root = true;
|
||||
if (!Vfs::mount($share['resolve_url'], $share['share_root'], false, false, !$keep_session))
|
||||
{
|
||||
sleep(1);
|
||||
$status = '404 Not Found';
|
||||
|
@ -244,8 +258,8 @@ class egw_sharing
|
|||
echo "Requested resource '/".htmlspecialchars($token)."' does NOT exist!\n";
|
||||
common::egw_exit();
|
||||
}
|
||||
egw_vfs::$is_root = false;
|
||||
egw_vfs::clearstatcache();
|
||||
Vfs::$is_root = false;
|
||||
Vfs::clearstatcache();
|
||||
|
||||
// update accessed timestamp
|
||||
self::$db->update(self::TABLE, array(
|
||||
|
@ -255,7 +269,7 @@ class egw_sharing
|
|||
), __LINE__, __FILE__);
|
||||
|
||||
// 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
|
||||
// --> create a new anon session
|
||||
|
@ -284,16 +298,16 @@ class egw_sharing
|
|||
$GLOBALS['egw']->session->commit_session();
|
||||
}
|
||||
// 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']['server']['vfs_fstab'] = egw_vfs::mount();
|
||||
$GLOBALS['egw_info']['user']['vfs_user'] = Vfs::$user;
|
||||
$GLOBALS['egw_info']['server']['vfs_fstab'] = Vfs::mount();
|
||||
|
||||
// update modified egw and egw_info again in session, if neccessary
|
||||
if ($keep_session || $sessionid)
|
||||
{
|
||||
$_SESSION[egw_session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
|
||||
unset($_SESSION[egw_session::EGW_INFO_CACHE]['flags']); // dont save the flags, they change on each request
|
||||
$_SESSION[Api\Session::EGW_INFO_CACHE] = $GLOBALS['egw_info'];
|
||||
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;
|
||||
|
@ -308,9 +322,9 @@ class egw_sharing
|
|||
*/
|
||||
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
|
||||
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)
|
||||
!file_exists(__DIR__.'/../../filemanager/inc/class.filemanager_ui.inc.php'));
|
||||
}
|
||||
|
@ -331,13 +345,13 @@ class egw_sharing
|
|||
if (!$this->use_filemanager())
|
||||
{
|
||||
// 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();
|
||||
$webdav_server = new vfs_webdav_server();
|
||||
$webdav_server->ServeRequest(egw_vfs::concat($this->share['share_root'], $this->share['share_token']));
|
||||
$webdav_server = new Vfs\WebDAV();
|
||||
$webdav_server->ServeRequest(Vfs::concat($this->share['share_root'], $this->share['share_token']));
|
||||
return;
|
||||
}
|
||||
// run full eTemplate2 UI for directories
|
||||
|
@ -346,7 +360,7 @@ class egw_sharing
|
|||
$_GET['cd'] = 'no';
|
||||
$GLOBALS['egw_info']['flags']['js_link_registry'] = true;
|
||||
egw_framework::includeCSS('filemanager', 'sharing');
|
||||
$ui = new egw_sharing_filemanager();
|
||||
$ui = new SharingUi();
|
||||
$ui->index();
|
||||
}
|
||||
|
||||
|
@ -357,13 +371,13 @@ class egw_sharing
|
|||
*/
|
||||
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 {
|
||||
$token = function_exists('openssl_random_pseudo_bytes') ?
|
||||
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 #
|
||||
} while ($token != egw_vfs::encodePathComponent($token));
|
||||
} while ($token != Vfs::encodePathComponent($token));
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
@ -387,7 +401,7 @@ class egw_sharing
|
|||
|
||||
if (empty($name)) $name = $path;
|
||||
|
||||
$path2tmp =& egw_cache::getSession(__CLASS__, 'path2tmp');
|
||||
$path2tmp =& Api\Cache::getSession(__CLASS__, 'path2tmp');
|
||||
|
||||
// allow filesystem path only for temp_dir
|
||||
$temp_dir = $GLOBALS['egw_info']['server']['temp_dir'].'/';
|
||||
|
@ -402,13 +416,13 @@ class egw_sharing
|
|||
{
|
||||
$path = 'vfs://default'.($path[0] == '/' ? '' : '/').$path;
|
||||
}
|
||||
$vfs_path = egw_vfs::parse_url($path, PHP_URL_PATH);
|
||||
$exists = egw_vfs::file_exists($vfs_path) && egw_vfs::is_readable($vfs_path);
|
||||
$vfs_path = Vfs::parse_url($path, PHP_URL_PATH);
|
||||
$exists = Vfs::file_exists($vfs_path) && Vfs::is_readable($vfs_path);
|
||||
}
|
||||
// check if file exists and is readable
|
||||
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
|
||||
if (($mode != self::LINK || isset($path2tmp[$path])) &&
|
||||
|
@ -447,29 +461,29 @@ class egw_sharing
|
|||
if ($mode == 'link')
|
||||
{
|
||||
$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;
|
||||
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) ||
|
||||
!is_dir($path) && (!egw_vfs::file_exists($tmp_file) && ($fp = egw_vfs::fopen($tmp_file, 'x')) ||
|
||||
while(!(is_dir($path) && Vfs::mkdir($tmp_file) ||
|
||||
!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
|
||||
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)
|
||||
{
|
||||
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 (is_dir($path) && !egw_vfs::copy_files(array($path), $tmp_file) ||
|
||||
!is_dir($path) && !copy($path, egw_vfs::PREFIX.$tmp_file))
|
||||
if (is_dir($path) && !Vfs::copy_files(array($path), $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
|
||||
$path2tmp[$path] = $tmp_file;
|
||||
|
@ -490,7 +504,7 @@ class egw_sharing
|
|||
try {
|
||||
self::$db->insert(self::TABLE, $share = array(
|
||||
'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_with' => implode(',', (array)$recipients),
|
||||
'share_created' => time(),
|
||||
|
@ -499,7 +513,7 @@ class egw_sharing
|
|||
$share['share_id'] = self::$db->get_last_insert_id(self::TABLE, 'share_id');
|
||||
break;
|
||||
}
|
||||
catch(egw_exception_db $e) {
|
||||
catch(Api\Db\Exception $e) {
|
||||
if ($i++ > 3) throw $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;
|
||||
|
||||
|
@ -522,7 +536,7 @@ class egw_sharing
|
|||
{
|
||||
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');
|
||||
}
|
||||
return self::$so;
|
||||
|
@ -567,7 +581,7 @@ class egw_sharing
|
|||
// if not delete them
|
||||
foreach($tmp_paths as $path)
|
||||
{
|
||||
egw_vfs::remove($path);
|
||||
Vfs::remove($path);
|
||||
}
|
||||
}
|
||||
return $deleted;
|
||||
|
@ -586,7 +600,7 @@ class egw_sharing
|
|||
public static function tmp_cleanup()
|
||||
{
|
||||
if (!isset(self::$db)) self::$db = $GLOBALS['egw']->db;
|
||||
egw_vfs::$is_root = true;
|
||||
Vfs::$is_root = true;
|
||||
|
||||
try {
|
||||
$cols = array(
|
||||
|
@ -607,7 +621,7 @@ class egw_sharing
|
|||
"share_path LIKE '/home/%/.tmp/%'",
|
||||
), __LINE__, __FILE__, false, 'GROUP BY share_path '.$having) as $row)
|
||||
{
|
||||
egw_vfs::remove($row['share_path']);
|
||||
Vfs::remove($row['share_path']);
|
||||
|
||||
if ($group_concat)
|
||||
{
|
||||
|
@ -629,10 +643,10 @@ class egw_sharing
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch (\Exception $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';
|
||||
|
||||
class egw_sharing_filemanager extends filemanager_ui
|
||||
class SharingUi extends filemanager_ui
|
||||
{
|
||||
/**
|
||||
* Get the configured start directory for the current user
|
|
@ -7,24 +7,30 @@
|
|||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage vfs
|
||||
* @subpackage webdav
|
||||
* @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
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
if (strpos(ini_get('include_path'), EGW_API_INC) === false)
|
||||
{
|
||||
ini_set('include_path', EGW_API_INC.PATH_SEPARATOR.ini_get('include_path'));
|
||||
}
|
||||
require_once('HTTP/WebDAV/Server/Filesystem.php');
|
||||
namespace EGroupware\Api\Vfs;
|
||||
|
||||
require_once dirname(__DIR__).'/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
|
||||
*
|
||||
* 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
|
||||
|
@ -39,7 +45,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
*
|
||||
* @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
|
||||
|
@ -90,11 +96,11 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
{
|
||||
// recursive delete the directory
|
||||
try {
|
||||
$deleted = egw_vfs::remove($options['path']);
|
||||
$deleted = Vfs::remove($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();
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +126,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
function MKCOL($options)
|
||||
{
|
||||
$path = $this->_unslashify($this->base .$options["path"]);
|
||||
$parent = egw_vfs::dirname($path);
|
||||
$parent = Vfs::dirname($path);
|
||||
|
||||
if (!file_exists($parent)) {
|
||||
return "409 Conflict";
|
||||
|
@ -225,7 +231,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
}
|
||||
} else {
|
||||
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 {
|
||||
$files = array($source);
|
||||
}
|
||||
|
@ -289,41 +295,41 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
$info['props'] = array();
|
||||
|
||||
// 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
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('creationdate', filectime($fspath));
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getlastmodified', filemtime($fspath));
|
||||
$info['props'][] = self::mkprop ('creationdate', filectime($fspath));
|
||||
$info['props'][] = self::mkprop ('getlastmodified', filemtime($fspath));
|
||||
|
||||
// Microsoft extensions: last access time and 'hidden' status
|
||||
$info["props"][] = HTTP_WebDAV_Server::mkprop("lastaccessed", fileatime($fspath));
|
||||
$info["props"][] = HTTP_WebDAV_Server::mkprop("ishidden", egw_vfs::is_hidden($fspath));
|
||||
$info["props"][] = self::mkprop("lastaccessed", fileatime($fspath));
|
||||
$info["props"][] = self::mkprop("ishidden", Vfs::is_hidden($fspath));
|
||||
|
||||
// type and size (caller already made sure that path exists)
|
||||
if (is_dir($fspath)) {
|
||||
// directory (WebDAV collection)
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', array(
|
||||
HTTP_WebDAV_Server::mkprop('collection', '')));
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory');
|
||||
$info['props'][] = self::mkprop ('resourcetype', array(
|
||||
self::mkprop('collection', '')));
|
||||
$info['props'][] = self::mkprop ('getcontenttype', 'httpd/unix-directory');
|
||||
} else {
|
||||
// plain file (WebDAV resource)
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', '');
|
||||
if (egw_vfs::is_readable($path)) {
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', egw_vfs::mime_content_type($path));
|
||||
$info['props'][] = self::mkprop ('resourcetype', '');
|
||||
if (Vfs::is_readable($path)) {
|
||||
$info['props'][] = self::mkprop ('getcontenttype', Vfs::mime_content_type($path));
|
||||
} else {
|
||||
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
|
||||
$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
|
||||
ToDo: return it only if explicitly requested ($options['props'])
|
||||
// supportedlock property
|
||||
$info['props'][] = HTTP_WebDAV_Server::mkprop('supportedlock','
|
||||
$info['props'][] = self::mkprop('supportedlock','
|
||||
<D:lockentry>
|
||||
<D:lockscope><D:exclusive/></D:lockscope>
|
||||
<D:locktype><D:write/></D:lockscope>
|
||||
|
@ -384,13 +390,13 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
$_path = $info['path'];
|
||||
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
|
||||
$path = egw_vfs::encodePath($_path);
|
||||
$path = Vfs::encodePath($_path);
|
||||
$path2n[$path] = $n;
|
||||
|
||||
// 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'];
|
||||
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)
|
||||
{
|
||||
$fileprops =& $files['files'][$path2n[$path]]['props'];
|
||||
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['name'] = substr($prop['name'],1);
|
||||
|
@ -437,13 +443,13 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
/**
|
||||
* 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
|
||||
* @return string
|
||||
*/
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
$path = translation::convert($options['path'],'utf-8');
|
||||
$path = Api\Translation::convert($options['path'],'utf-8');
|
||||
|
||||
foreach ($options['props'] as $key => $prop) {
|
||||
$attributes = array();
|
||||
|
@ -492,14 +498,14 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
{
|
||||
case 'srt_modifiedtime':
|
||||
case 'getlastmodified':
|
||||
egw_vfs::touch($path,strtotime($prop['val']));
|
||||
Vfs::touch($path,strtotime($prop['val']));
|
||||
break;
|
||||
//case 'srt_creationtime':
|
||||
// no streamwrapper interface / php function to set the ctime currently
|
||||
//$attributes['created'] = strtotime($prop['val']);
|
||||
//break;
|
||||
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;
|
||||
|
@ -509,7 +515,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
{
|
||||
// allow netdrive to change the modification time
|
||||
case 'getlastmodified':
|
||||
egw_vfs::touch($path,strtotime($prop['val']));
|
||||
Vfs::touch($path,strtotime($prop['val']));
|
||||
break;
|
||||
// not sure why, the filesystem example of the WebDAV class does it ...
|
||||
default:
|
||||
|
@ -522,23 +528,23 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
switch($prop['name'])
|
||||
{
|
||||
case 'Win32LastModifiedTime':
|
||||
egw_vfs::touch($path,strtotime($prop['val']));
|
||||
Vfs::touch($path,strtotime($prop['val']));
|
||||
break;
|
||||
case 'Win32CreationTime': // eg. "Wed, 14 Sep 2011 15:48:26 GMT"
|
||||
case 'Win32LastAccessTime':
|
||||
case 'Win32FileAttributes': // not sure what that is, it was always "00000000"
|
||||
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;
|
||||
|
||||
case egw_vfs::DEFAULT_PROP_NAMESPACE.'customfields/': // eGW's customfields
|
||||
$prop['ns'] = egw_vfs::DEFAULT_PROP_NAMESPACE;
|
||||
case Vfs::DEFAULT_PROP_NAMESPACE.'customfields/': // eGW's customfields
|
||||
$prop['ns'] = Vfs::DEFAULT_PROP_NAMESPACE;
|
||||
$prop['name'] = '#'.$prop['name'];
|
||||
// fall through
|
||||
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;
|
||||
}
|
||||
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
|
||||
// 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']))
|
||||
{
|
||||
return $ret ? '200 OK' : '409 Conflict';
|
||||
|
@ -587,7 +593,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
function UNLOCK(&$options)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
@ -650,7 +656,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
if (($ok = parent::GET($options)))
|
||||
{
|
||||
// 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
|
||||
|
||||
if (!is_resource($options['stream']))
|
||||
|
@ -678,7 +684,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
{
|
||||
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 "\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".
|
||||
|
@ -691,7 +697,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
|
|||
foreach(explode('/',$this->_unslashify($options['path'])) as $n => $name)
|
||||
{
|
||||
$path .= ($n != 1 ? '/' : '').$name;
|
||||
echo html::a_href(htmlspecialchars($name.'/'),$path);
|
||||
echo Api\Html::a_href(htmlspecialchars($name.'/'),$path);
|
||||
}
|
||||
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>".
|
||||
html::a_href(htmlspecialchars($name),$base.strtr($file['path'], array(
|
||||
Api\Html::a_href(htmlspecialchars($name),$base.strtr($file['path'], array(
|
||||
'%' => '%25',
|
||||
'#' => '%23',
|
||||
'?' => '%3F',
|
|
@ -33,9 +33,9 @@
|
|||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
require_once "HTTP/WebDAV/Tools/_parse_propfind.php";
|
||||
require_once "HTTP/WebDAV/Tools/_parse_proppatch.php";
|
||||
require_once "HTTP/WebDAV/Tools/_parse_lockinfo.php";
|
||||
require_once __DIR__."/Tools/_parse_propfind.php";
|
||||
require_once __DIR__."/Tools/_parse_proppatch.php";
|
||||
require_once __DIR__."/Tools/_parse_lockinfo.php";
|
||||
|
||||
/**
|
||||
* Virtual base class for implementing WebDAV servers
|
|
@ -33,8 +33,7 @@
|
|||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
require_once "HTTP/WebDAV/Server.php";
|
||||
require_once "System.php";
|
||||
require_once dirname(__DIR__).'/Server.php';
|
||||
|
||||
/**
|
||||
* Filesystem access using WebDAV
|
|
@ -5,11 +5,14 @@
|
|||
* @link http://www.egroupware.org/
|
||||
* @package filemanager
|
||||
* @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
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
use EGroupware\Api\Vfs\Sharing;
|
||||
|
||||
/**
|
||||
* Filemanager: shares
|
||||
*/
|
||||
|
@ -40,7 +43,7 @@ class filemanager_shares extends filemanager_ui
|
|||
{
|
||||
// sudo handling
|
||||
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/';
|
||||
}
|
||||
|
||||
|
@ -57,16 +60,16 @@ class filemanager_shares extends filemanager_ui
|
|||
{
|
||||
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.'%');
|
||||
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_writable'] = false;
|
||||
break;
|
||||
|
||||
case egw_sharing::WRITABLE:
|
||||
case Sharing::WRITABLE:
|
||||
$query['col_filter']['share_writable'] = true;
|
||||
break;
|
||||
}
|
||||
|
@ -82,18 +85,18 @@ class filemanager_shares extends filemanager_ui
|
|||
$query['col_filter']['share_owner'] = $GLOBALS['egw_info']['user']['account_id'];
|
||||
|
||||
$readonlys = null;
|
||||
$total = egw_sharing::so()->get_rows($query, $rows, $readonlys);
|
||||
$total = Sharing::so()->get_rows($query, $rows, $readonlys);
|
||||
|
||||
foreach($rows as &$row)
|
||||
{
|
||||
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['type'] = egw_sharing::LINK;
|
||||
$row['type'] = Sharing::LINK;
|
||||
}
|
||||
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'];
|
||||
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'];
|
||||
}
|
||||
egw_framework::message(lang('%1 shares deleted.', egw_sharing::delete($where)), 'success');
|
||||
egw_framework::message(lang('%1 shares deleted.', Sharing::delete($where)), 'success');
|
||||
break;
|
||||
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']);
|
||||
}
|
||||
$content['is_setup'] = self::$is_setup;
|
||||
|
||||
$sel_options = array(
|
||||
'type' => egw_sharing::$modes,
|
||||
'type' => Sharing::$modes,
|
||||
'share_passwd' => array(
|
||||
'no' => lang('No'),
|
||||
'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);
|
||||
}
|
||||
}
|
|
@ -13,10 +13,13 @@
|
|||
* @package api
|
||||
* @subpackage vfs
|
||||
* @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$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
use EGroupware\Vfs;
|
||||
|
||||
$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);
|
||||
}
|
||||
return egw_digest_auth::autocreate_session_callback($account);
|
||||
return Api\Header\Authenticate::autocreate_session_callback($account);
|
||||
}
|
||||
|
||||
$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',
|
||||
'autocreate_session_callback' => 'check_access',
|
||||
'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!
|
||||
try
|
||||
{
|
||||
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']))
|
||||
{
|
||||
|
@ -73,18 +75,18 @@ catch (egw_exception_no_permission_app $e)
|
|||
// temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted
|
||||
// so ownCloud dir contains users home-dir by default
|
||||
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;
|
||||
egw_vfs::$is_root = true;
|
||||
$ok = egw_vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
|
||||
egw_vfs::$is_root = $is_root_backup;
|
||||
$is_root_backup = Vfs::$is_root;
|
||||
Vfs::$is_root = true;
|
||||
$ok = Vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
|
||||
Vfs::$is_root = $is_root_backup;
|
||||
//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
|
||||
$GLOBALS['egw']->session->commit_session();
|
||||
|
||||
$webdav_server = new vfs_webdav_server();
|
||||
$webdav_server = new Vfs\WebDAV();
|
||||
$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));
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* @package api
|
||||
* @subpackage groupdav
|
||||
* @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$
|
||||
*/
|
||||
|
||||
|
@ -36,13 +36,12 @@ $GLOBALS['egw_info'] = array(
|
|||
'noheader' => True,
|
||||
'currentapp' => 'groupdav',
|
||||
'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!
|
||||
)
|
||||
);
|
||||
// if you move this file somewhere else, you need to adapt the path to the header!
|
||||
$egw_dir = dirname(__FILE__);
|
||||
require_once($egw_dir.'/phpgwapi/inc/class.egw_digest_auth.inc.php');
|
||||
include($egw_dir.'/header.inc.php');
|
||||
|
||||
$GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* eGroupWare API: Basic and Digest Auth
|
||||
* EGroupware API: Basic and Digest Auth
|
||||
*
|
||||
* For Apache FCGI you need the following rewrite rule:
|
||||
*
|
||||
|
@ -15,256 +15,15 @@
|
|||
* @package api
|
||||
* @subpackage auth
|
||||
* @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$
|
||||
*/
|
||||
|
||||
use EGroupware\Api\Header\Authenticate;
|
||||
|
||||
/**
|
||||
* 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' => 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.)
|
||||
* @deprecated use EGroupware\Api\Header\Authenticate
|
||||
*/
|
||||
class egw_digest_auth
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
class egw_digest_auth extends Authenticate {}
|
||||
|
|
|
@ -7,15 +7,11 @@
|
|||
* @package api
|
||||
* @subpackage groupdav
|
||||
* @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$
|
||||
*/
|
||||
|
||||
if (strpos(ini_get('include_path'), EGW_API_INC) === false)
|
||||
{
|
||||
ini_set('include_path', EGW_API_INC.PATH_SEPARATOR.ini_get('include_path'));
|
||||
}
|
||||
require_once('HTTP/WebDAV/Server.php');
|
||||
require_once(EGW_INCLUDE_ROOT.'/api/src/WebDAV/Server.php');
|
||||
|
||||
/**
|
||||
* EGroupware: GroupDAV access
|
||||
|
|
24
remote.php
24
remote.php
|
@ -13,10 +13,13 @@
|
|||
* @package api
|
||||
* @subpackage vfs
|
||||
* @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$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
use EGroupware\Vfs;
|
||||
|
||||
$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);
|
||||
}
|
||||
return egw_digest_auth::autocreate_session_callback($account);
|
||||
return Api\Header\Authenticate::autocreate_session_callback($account);
|
||||
}
|
||||
|
||||
$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',
|
||||
'autocreate_session_callback' => 'check_access',
|
||||
'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!
|
||||
try
|
||||
{
|
||||
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']))
|
||||
{
|
||||
|
@ -73,18 +75,18 @@ catch (egw_exception_no_permission_app $e)
|
|||
// temporary mount ownCloud default /clientsync as /home/$user, if not explicitly mounted
|
||||
// so ownCloud dir contains users home-dir by default
|
||||
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;
|
||||
egw_vfs::$is_root = true;
|
||||
$ok = egw_vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
|
||||
egw_vfs::$is_root = $is_root_backup;
|
||||
$is_root_backup = Vfs::$is_root;
|
||||
Vfs::$is_root = true;
|
||||
$ok = Vfs::mount($url='vfs://default/home/$user', $clientsync='/clientsync', null, false);
|
||||
Vfs::$is_root = $is_root_backup;
|
||||
//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
|
||||
$GLOBALS['egw']->session->commit_session();
|
||||
|
||||
$webdav_server = new vfs_webdav_server();
|
||||
$webdav_server = new Vfs\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));
|
||||
|
|
10
share.php
10
share.php
|
@ -6,11 +6,13 @@
|
|||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @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$
|
||||
*/
|
||||
|
||||
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(
|
||||
'flags' => array(
|
||||
|
@ -18,7 +20,7 @@ $GLOBALS['egw_info'] = array(
|
|||
'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',
|
||||
'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)
|
||||
)
|
||||
);
|
||||
|
@ -27,6 +29,6 @@ include('./header.inc.php');
|
|||
|
||||
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();
|
||||
|
|
14
webdav.php
14
webdav.php
|
@ -16,10 +16,13 @@
|
|||
* @package api
|
||||
* @subpackage vfs
|
||||
* @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$
|
||||
*/
|
||||
|
||||
use EGroupware\Api;
|
||||
use EGroupware\Api\Vfs;
|
||||
|
||||
$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);
|
||||
}
|
||||
return egw_digest_auth::autocreate_session_callback($account);
|
||||
return Api\Header\Authenticate::autocreate_session_callback($account);
|
||||
}
|
||||
|
||||
$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',
|
||||
'autocreate_session_callback' => 'check_access',
|
||||
'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!
|
||||
try
|
||||
{
|
||||
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']))
|
||||
{
|
||||
|
@ -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
|
||||
$GLOBALS['egw']->session->commit_session();
|
||||
|
||||
$webdav_server = new vfs_webdav_server();
|
||||
$webdav_server = new Vfs\WebDAV();
|
||||
$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));
|
||||
|
|
Loading…
Reference in New Issue