openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 29da9f18b431d826605fcdd3161fe21e5c3c856a
parent 0a56bd3d64ddc1be1edc85c0780836a3b9317523
Author: Jan Dankert <devnull@localhost>
Date:   Mon,  9 Jan 2017 23:40:27 +0100

Initiale Version der WebDAV-Anbindung per HTTP-API: Login funktioniert, Rest noch nicht :(

Diffstat:
dav/CMS.class.php | 30++++++++++++++++++++++++++++++
dav/Client.class.php | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dav/Logger.class.php | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dav/dav.ini | 12++++++++++++
dav/dav.log | 44++++++++++++++++++++++++++++++++++++++++++++
dav/dav.php | 1221++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
dav/index.php | 2++
7 files changed, 1628 insertions(+), 0 deletions(-)

diff --git a/dav/CMS.class.php b/dav/CMS.class.php @@ -0,0 +1,29 @@ +<?php + + +class CMS extends Client +{ + var $login = false; + var $token; + + function login($user, $password,$dbid ) + { + + $this->action = 'login'; + $this->subaction = 'login'; + $this->method = 'GET'; + + $result = $this->call('GET','login','login' ); + // TODO: Read database Ids from $result + + $this->action = 'login'; + $this->subaction = 'login'; + $this->method = 'POST'; + + $result = $this->call('POST','login','login',array('login_name'=>$user,'login_password'=>$password,'dbid'=>$dbid) ); + + return( $result['success'] == 'true' ); + } +} + +?>+ \ No newline at end of file diff --git a/dav/Client.class.php b/dav/Client.class.php @@ -0,0 +1,178 @@ +<?php + + +class Client +{ + protected $action; + protected $subaction; + protected $cookies = array(); + protected $useCookies = false; + + protected $sessionName; + protected $sessionId; + protected $token; + + protected $method; // GET oder POST + + protected $responseHeader; + + + protected function call($method,$action,$subaction,$parameter=array()) + { + global $config; + $error = ''; + $status = ''; + + $errno = 0; + $errstr = ''; + + $host = $config['cms.host']; + $port = $config['cms.port']; + $path = $config['cms.path']; + + // Vorbedingungen checken: + // Slash an Anfang und Ende? + if ( substr($path,-1 ) != '/' ) + $path = $path.'/'; + if ( substr($path,0,1 ) != '/' ) + $path = '/'.$path; + $path .= '/dispatcher.php'; + + // Methode: Fallback GET + if ( empty($method)) + $method='GET'; + + // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig). + if ( $port == '443' || @$config['ssl'] ) + $prx_proto = 'ssl://'; // SSL + else + $prx_proto = 'tcp://'; // Default + + $fp = fsockopen ($prx_proto.$host,$port, $errno, $errstr, 30); + + if ( !$fp || !is_resource($fp) ) + { + echo "Connection refused: '".$prx_proto.$host.':'.$port." - $errstr ($errno)"; + } + else + { + $lb = "\r\n"; + $http_get = $path; + + $parameter += array('action'=>$action,'subaction'=>$subaction); + if ( $method=='POST') + $parameter += array('token'=>$this->token); + + $parameterString = ''; + + foreach( $parameter as $name=>$value ) + { + if ( !empty($parameterString) ) + $parameterString .= '&'; + + $parameterString .= urlencode($name).'='.urlencode($value); + } + + if ( $method == 'GET') + $http_get .= '?'.$parameterString; + + $header = array(); + + $header[] = $method.' '.$http_get.' HTTP/1.0'; + $header[] = 'Host: '.$host; + $header[] = 'Accept: application/php-serialized'; + + if ( $this->useCookies) + foreach( $this->cookies as $cookieName=>$cookieValue) + $header[] = 'Cookie: '.$cookieName.'='.$cookieValue; + + if ( ! empty($this->sessionName)) + $header[] = 'Cookie: '.$this->sessionName.'='.$this->sessionId; + + if ( $method == 'POST' ) + { + $header[] = 'Content-Type: application/x-www-form-urlencoded'; + $header[] = 'Content-Length: '.strlen($parameterString); + } + + $http_request = implode($lb,$header).$lb.$lb; + + if ( $method == 'POST' ) + { + $http_request .= $parameterString; + } + if (!is_resource($fp)) { + $error = 'Connection lost after connect: '.$prx_proto.$host.':'.$port; + return false; + } + fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden. + + // Jetzt erfolgt das Auslesen der HTTP-Antwort. + $isHeader = true; + + // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor + // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP + if (!is_resource($fp)) { + echo 'Connection lost during transfer: '.$host.':'.$port; + } + elseif (!feof($fp)) { + $line = fgets($fp,1028); + $status = substr($line,9,3); + } + else + { + echo 'Unexpected EOF while reading HTTP-Response'; + } + + $body=''; + while (!feof($fp)) { + $line = fgets($fp,1028); + if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header. + { + $isHeader = false; + } + elseif( $isHeader ) + { + list($headerName,$headerValue) = explode(': ',$line) + array(1=>''); + $this->responseHeader[$headerName] = trim($headerValue); + } + else + { + $body .= $line; + } + } + fclose($fp); // Verbindung brav schlie�en. + + foreach( $this->responseHeader as $headerName => $headerValue) + { + if ( $headerName == 'Set-Cookie' ) + { + $parts = explode(';',$headerValue); + $payload = $parts[0]; + list( $cookieName,$cookieValue) = explode('=',$payload); + { + $this->cookies[trim($cookieName)] = trim($cookieValue); + } + } + } + + $result = unserialize($body); + if ( $result === false ) + { + error_log('Not unserializable: '.$body); + httpStatus('500 Server Error, not unserializable: '.$body); + exit; + } + else + { + $this->sessionName = $result['session']['name']; + $this->sessionId = $result['session']['id']; + $this->token = $result['session']['token']; +// var_dump($result); + return $result; + } + + } + } +} +?>+ \ No newline at end of file diff --git a/dav/Logger.class.php b/dav/Logger.class.php @@ -0,0 +1,139 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +define('DAV_LOG_LEVEL_TRACE',5 ); +define('DAV_LOG_LEVEL_DEBUG',4 ); +define('DAV_LOG_LEVEL_INFO' ,3 ); +define('DAV_LOG_LEVEL_WARN' ,2 ); +define('DAV_LOG_LEVEL_ERROR',1 ); + +define('DAV_LOG_LEVEL', constant('DAV_LOG_LEVEL_'.strtoupper($config['log.level']))); + + +/** + * Schreiben eines Eintrages in die Logdatei + * + * @author $Author$ + * @version $Rev: $ + * @package openrat.services + */ +class Logger +{ + /** + * Schreiben einer Trace-Meldung in die Logdatei + * + * @param message Log-Text + */ + function trace( $message ) + { + if ( DAV_LOG_LEVEL >= DAV_LOG_LEVEL_TRACE ) + Logger::doLog( 'trace',$message ); + } + + + /** + * Schreiben einer Debug-Meldung in die Logdatei + * + * @param message Log-Text + */ + function debug( $message ) + { + if ( DAV_LOG_LEVEL >= DAV_LOG_LEVEL_DEBUG ) + Logger::doLog( 'debug',$message ); + } + + + /** + * Schreiben einer Information in die Logdatei + * + * @param message Log-Text + */ + function info( $message ) + { + if ( DAV_LOG_LEVEL >= DAV_LOG_LEVEL_INFO ) + Logger::doLog( 'info',$message ); + } + + + /** + * Schreiben einer Warnung in die Logdatei + * + * @param message Log-Text + */ + function warn( $message ) + { + if ( DAV_LOG_LEVEL >= DAV_LOG_LEVEL_WARN ) + Logger::doLog( 'warn',$message ); + } + + + /** + * Schreiben einer normalen Fehlermeldung in die Logdatei + * + * @param message Log-Text + */ + function error( $message ) + { + if ( DAV_LOG_LEVEL >= DAV_LOG_LEVEL_ERROR ) + Logger::doLog( 'error',$message ); + } + + + /** + * Schreiben der Meldung in die Logdatei + * + * @author $Author$ + * @param facility Schwere dieses Logdatei-Eintrages (z.B. warning) + * @param message Log-Text + * @access private + */ + function doLog( $facility,$message ) + { + global $config; + + $filename = $config['log.file']; + + if ( empty($filename) ) + return; + + if ( ! is_writable($filename) ) + Http::serverError( "logfile $filename is not writable by the server" ); + + $thisLevel = strtoupper($facility); + + $text = "%time %level %host %action %text"; // Format der Logdatei + + // Ersetzen von Variablen + $text = str_replace( '%host',getenv('REMOTE_ADDR'),$text ); + + $action = strtoupper($_SERVER['REQUEST_METHOD']); + + $text = str_replace( '%level' ,str_pad($thisLevel,5),$text ); + $text = str_replace( '%agent' ,getenv('HTTP_USER_AGENT'),$text ); + $text = str_replace( '%action',str_pad($action,12),$text ); + $text = str_replace( '%text' ,$message,$text ); + $text = str_replace( '%time' ,date('M j H:i:s'),$text ); + $text = str_replace( "\n" ,"\n ",$text ); + + // Schreiben in Logdatei + error_log( $text."\n",3,$filename ); + } +} + +?>+ \ No newline at end of file diff --git a/dav/dav.ini b/dav/dav.ini @@ -0,0 +1,12 @@ +dav.enable=true +dav.create=true +dav.readonly = true +log.file=dav.log +log.level=trace + +dav.anonymous = false +cms.username=dankert +cms.password=dankert +cms.database=pdo_pg_prod +cms.host=duese +cms.path="/~dankert/cms/cms09/" diff --git a/dav/dav.log b/dav/dav.log @@ -0,0 +1,44 @@ +Jan 9 22:49:58 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:49:58 WARN 127.0.0.1 OPTIONS WEBDAV is disabled by configuration +Jan 9 22:50:50 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:50:50 WARN 127.0.0.1 OPTIONS WEBDAV is disabled by configuration +Jan 9 22:55:33 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:55:37 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:55:47 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:55:50 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:57:25 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 22:57:28 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:04:17 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:04:19 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:04:26 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:14:14 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:14:19 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:14:27 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:17:48 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:17:51 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:18:19 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:19:21 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:19:30 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:11 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:16 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:34 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:36 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:38 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:26:41 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:27:46 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:27:46 TRACE 127.0.0.1 OPTIONS OPTIONS: Allow: OPTIONS, HEAD, GET, PROPFIND +Jan 9 23:35:20 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:35:20 TRACE 127.0.0.1 OPTIONS OPTIONS: Allow: OPTIONS, HEAD, GET, PROPFIND +Jan 9 23:35:20 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:35:24 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:35:32 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:35:32 TRACE 127.0.0.1 OPTIONS OPTIONS: Allow: OPTIONS, HEAD, GET, PROPFIND +Jan 9 23:35:32 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:35:35 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:35:35 TRACE 127.0.0.1 OPTIONS OPTIONS: Allow: OPTIONS, HEAD, GET, PROPFIND +Jan 9 23:35:35 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:35:43 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:37:38 TRACE 127.0.0.1 OPTIONS WEBDAV request +Jan 9 23:37:38 TRACE 127.0.0.1 OPTIONS OPTIONS: Allow: OPTIONS, HEAD, GET, PROPFIND +Jan 9 23:37:38 TRACE 127.0.0.1 PROPFIND WEBDAV request +Jan 9 23:37:42 TRACE 127.0.0.1 PROPFIND WEBDAV request diff --git a/dav/dav.php b/dav/dav.php @@ -0,0 +1,1220 @@ +<?php + +/** + * WebDAV für OpenRat Content Management System<br> + * + * Das virtuelle Ordnersystem dieses CMS kann ueber das WebDAV-Protokoll + * dargestellt werden. + * + * Diese Klasse nimmt die Anfragen von WebDAV-Clients entgegen, zerlegt die + * Anfrage und erzeugt eine Antwort, die im HTTP-Body zurueck uebertragen + * wird. + * <br> + * WebDAV ist spezifiziert in der RFC 2518.<br> + * Siehe <code>http://www.ietf.org/rfc/rfc2518.txt</code><br> + * + * Implementiert wird DAV-Level 1 (d.h. ohne LOCK). + * + * Der Zugang über WebDAV beinhaltet einige Nachteile: + * - Login ist nur mit Name/Kennwort möglich (kein OpenId) + * - Nur die Standard-Datenbank kann verwendet werden + * - Der Client muss Cookies unterstützen + * + * @author Jan Dankert + * @package openrat.actions + */ + + + +if (!defined('E_STRICT')) + define('E_STRICT', 2048); + + +$defConfig = array('dav.enable' => false, + 'dav.create' => true, + 'dav.readonly' => false, + 'dav.expose_openrat' => true, + 'dav.compliant_to_redmond' => true, + 'dav.realm' =>'OpenRat CMS WebDAV Login', + 'dav.anonymous' => false, + 'cms.host' => 'localhost', + 'cms.port' => 80, + 'cms.username' => null, + 'cms.password' => null, + 'cms.database' => 'db1', + 'cms.path' => '/', + 'cms.max_file_size' => 1000, + 'log.level' => 'info', + 'log.file' => null + ); + +$config = parse_ini_file('dav.ini') + $defConfig; + + + +// PHP-Fehler ins Log schreiben, damit die Ausgabe nicht zerstoert wird. +if (version_compare(PHP_VERSION, '5.0.0', '>')) + set_error_handler('webdavErrorHandler',E_ERROR | E_WARNING); +else + set_error_handler('webdavErrorHandler'); + +require('Logger.class.php'); +require('Client.class.php'); +require('CMS.class.php'); + +$httpMethod = strtoupper($_SERVER['REQUEST_METHOD']); +$davMethodName = 'dav'.$httpMethod; + + +$dav = new WebDAV(); + +$dav->$davMethodName(); + +class WebDAV +{ + // Zahlreiche Instanzvariablen, die im Konstruktor + // beim Zerlegen der Anfrag gef�llt werden. + var $defaultSubAction = 'show'; + var $database; + var $depth; + var $project; + var $folder; + var $obj; + var $filename; + var $pathnames = array(); + var $uri; + var $headers; + var $requestType; + var $request; + var $destination = null; + var $fullSkriptName; + var $create; + var $readonly; + var $maxFileSize; + var $webdav_conf; + var $overwrite = false; + + + /** + * Im Kontruktor wird der Request analysiert und ggf. eine Authentifzierung + * durchgefuehrt. + */ + function WebDAV() + { + global $config; + + Logger::trace( 'WEBDAV request' ); + + if ( $config['dav.compliant_to_redmond'] ) + header('MS-Author-Via: DAV' ); // Extrawurst fuer MS-Clients. + + if ( $config['dav.expose_openrat'] ) + header('X-Dav-powered-by: OpenRat CMS'); // Bandbreite verschwenden :) + +// Logger::trace( 'WEBDAV: URI='.$_SERVER['REQUEST_URI']); + + if ( !$config['dav.enable']) + { + Logger::warn( 'WEBDAV is disabled by configuration' ); + + $this->httpStatus('403 Forbidden'); + exit; + } + + $this->create = $config['dav.create']; + $this->readonly = $config['dav.readonly']; + $this->maxFileSize = $config['cms.max_file_size']; + + $this->headers = getallheaders(); + /* DAV compliant servers MUST support the "0", "1" and + * "infinity" behaviors. By default, the PROPFIND method without a Depth + * header MUST act as if a "Depth: infinity" header was included. */ + if ( !isset($this->headers['Depth']) ) + $this->depth = 1; + elseif ( strtolower($this->headers['Depth'])=='infinity') + $this->depth = 1; + else + $this->depth = intval($this->headers['Depth']); + + if ( isset($this->headers['Destination']) ) + $this->destination = $this->headers['Destination']; + + if ( isset($this->headers['Overwrite']) ) + $this->overwrite = $this->headers['Overwrite'] == 'T'; + + + session_start(); + if ( !empty($_SESSION['DAV_CLIENT']) ) + $client = $_SESSION['DAV_CLIENT']; + else + { + $client = new CMS(); + $_SESSION['DAV_CLIENT'] = $client; + } + + if ( $client->login ) + { + // Benutzer ist bereits im CMS eingeloggt. + } + else + { + // Login + global $httpMethod; + if ( $httpMethod != 'OPTIONS' ) // Bei OPTIONS kein Login anfordern + { + if ( isset($_SERVER['PHP_AUTH_USER']) ) + { + + $username = $_SERVER['PHP_AUTH_USER']; + $pass = $_SERVER['PHP_AUTH_PW' ]; + + $loginOk = $client->login($username, $pass, $config['cms.database']); + if ( !$loginOk) { + // Login falsch + $this->httpStatus('401 Unauthorized'); + header('WWW-Authenticate: Basic realm="'.$config['dav.realm'].'"'); + echo 'Failed login for user '.$username; + exit; + } + } + elseif ( $config['dav.anonymous']) + { + $username = $config['cms.user']; + $pass = $config['cms.password']; + + $loginOk = $client->login($username, $pass, $config['cms.database']); + if ( !$loginOk ) { + $this->httpStatus('500 Internal Server Error'); + echo 'Could not authenticate user '.$username; + exit; + } + } + else + { + // Client ist nicht angemeldet, daher wird nun die + // Authentisierung angefordert. + header('WWW-Authenticate: Basic realm="'.$config['dav.realm'].'"'); + $this->httpStatus('401 Unauthorized'); + echo 'Authentification required for '.$config['dav.realm']; + exit; + + } + } + else + { + return; // + } + } + + + $this->fullSkriptName = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'/'; + + // URL parsen. + $uri = substr($_SERVER['REQUEST_URI'],strlen($_SERVER['SCRIPT_NAME'])); + + $uri = $this->parseURI( $uri ); + $this->requestType = $uri['type' ]; + $this->folder = $uri['folder' ]; + $this->obj = $uri['object' ]; + $this->project = $uri['project']; + + $this->fullSkriptName .= implode('/',$uri['path']); + + if ( is_object($this->obj) && $this->obj->isFolder ) + $this->fullSkriptName .= '/'; + + /* + * Verzeichnisse muessen mit einem '/' enden. Falls nicht, Redirect aussfuehren. + * + * RFC 2518, 5.2 Collection Resources, Page 11: + * "For example, if a client invokes a + * method on http://foo.bar/blah (no trailing slash), the resource + * http://foo.bar/blah/ (trailing slash) may respond as if the operation + * were invoked on it, and should return a content-location header with + * http://foo.bar/blah/ in it. In general clients SHOULD use the "/" + * form of collection names." + */ + if ( is_object($this->obj) && + $this->obj->isFolder && + $_GET['subaction'] == 'get' && + substr($_SERVER['REQUEST_URI'],strlen($_SERVER['REQUEST_URI'])-1 ) != '/' ) + { + Logger::debug( 'WebDAV: Redirecting lame client to slashyfied URL' ); + + header('HTTP/1.1 302 Moved Temporarily'); + header('Location: '.$_SERVER['REQUEST_URI'].'/'); + exit; + } + + // Falls vorhanden, den "Destination"-Header parsen. + if ( isset($_SERVER['HTTP_DESTINATION']) ) + { + $destUri = parse_url( $_SERVER['HTTP_DESTINATION'] ); + + $uri = substr($destUri['path'],strlen($_SERVER['SCRIPT_NAME'])+$sos); + + // URL parsen. + $this->destination = $this->parseURI( $uri ); + } + + // Den Request-BODY aus der Standardeingabe lesen. + $this->request = implode('',file('php://input')); + } + + + + private function allowed_methods() + { + + if ($this->readonly) + return array('OPTIONS','HEAD','GET','PROPFIND'); // Readonly-Modus + else + // PROPPATCH unterstuetzen wir garnicht, aber lt. Spec sollten wir das. + return array('OPTIONS','HEAD','GET','PROPFIND','DELETE','PUT','COPY','MOVE','MKCOL','PROPPATCH'); + } + + + + /** + * HTTP-Methode OPTIONS.<br> + * <br> + * Es werden die verfuegbaren Methoden ermittelt und ausgegeben. + */ + public function davOPTIONS() + { + header('DAV: 1'); // Wir haben DAV-Level 1. + header('Allow: '.implode(', ',$this->allowed_methods()) ); + + Logger::trace('OPTIONS: '.'Allow: '.implode(', ',$this->allowed_methods())); + + $this->httpStatus( '200 OK' ); + } + + + + /** + * Setzt einen HTTP-Status.<br> + * <br> + * Es wird ein HTTP-Status gesetzt, zus�tzlich wird der Status in den Header "X-WebDAV-Status" geschrieben.<br> + * Ist der Status nicht 200 oder 207 (hier folgt ein BODY), wird das Skript beendet. + */ + public function httpStatus( $status = true ) + { + httpStatus($status); + } + + + + /** + * WebDav-HEAD-Methode. + */ + public function davHEAD() + { + if ( $this->obj == null ) + { + $this->httpStatus( '404 Not Found' ); + } + elseif ( $this->obj->isFolder ) + { + $this->httpStatus( '200 OK' ); + } + elseif( $this->obj->isPage ) + { + $this->httpStatus( '200 OK' ); + } + elseif( $this->obj->isLink ) + { + $this->httpStatus( '200 OK' ); + } + elseif( $this->obj->isFile ) + { + $this->httpStatus( '200 OK' ); + } + } + + + + /** + * WebDav-GET-Methode. + * Die gew�nschte Datei wird geladen und im HTTP-Body mitgeliefert. + */ + public function davGET() + { + if ( $this->obj->isFolder ) + $this->getDirectory(); + elseif( $this->obj->isPage ) + { + $this->httpStatus( '200 OK' ); + + header('Content-Type: text/html'); + + $page = new Page( $this->obj->objectid ); + $page->load(); + echo '<html><head><title>OpenRat WEBDAV Access</title></head>'; + echo '<body>'; + echo '<h1>'.$page->full_filename().'</h1>'; + echo '<pre>'; + echo 'No Content available'; + echo '</pre>'; + echo '</body>'; + echo '</html>'; + } + elseif( $this->obj->isLink ) + { + $this->httpStatus( '200 OK' ); + + header('Content-Type: text/plain'); + + $link = new Link( $this->obj->objectid ); + $link->load(); + echo 'url: ' .$link->url ."\n"; + echo 'target-id: '.$link->linkedObjectId."\n"; + } + elseif( $this->obj->isFile ) + { + $this->httpStatus( '200 OK' ); + + $file = new File( $this->obj->objectid ); + $file->load(); + + header('Content-Type: '.$file->mimeType() ); + header('X-File-Id: '.$file->fileid ); + + // Angabe Content-Disposition + // - Bild soll "inline" gezeigt werden + // - Dateiname wird benutzt, wenn der Browser das Bild speichern moechte + header('Content-Disposition: inline; filename='.$file->filenameWithExtension() ); + header('Content-Transfer-Encoding: binary' ); + header('Content-Description: '.$file->name ); + + $file->write(); // Bild aus Datenbank laden und in tempor�re Datei schreiben + + // Groesse des Bildes in Bytes + // Der Browser hat so die Moeglichkeit, einen Fortschrittsbalken zu zeigen + header('Content-Length: '.filesize($file->tmpfile()) ); + readfile( $file->tmpfile() ); + } + } + + + + /** + * Erzeugt ein Unix-�hnliche Ausgabe des Verzeichnisses als HTML. + */ + private function getDirectory() + { + $this->httpStatus( '200 OK' ); + + // Verzeichnis ausgeben + header('Content-Type: text/html'); + $nl = "\n"; + $titel = 'Index of '.htmlspecialchars($this->fullSkriptName); + $format = "%15s %-19s %-s\n"; + + echo '<html><head><title>'.$titel.'</title></head>'; + echo '<body>'; + echo '<h1>'.$titel.'</h1>'.$nl; + echo '<pre>'; + + printf($format, "Size", "Last modified", "Filename"); + + if ( $this->requestType == 'projectlist' ) + { + foreach( Project::getAll() as $projectName ) + { + $objektinhalt = array(); + $z = 30*365.25*24*60*60; + $objektinhalt['createdate' ] = $z; + $objektinhalt['lastchangedate'] = $z; + $objektinhalt['size' ] = 1; + echo '<a href="'.$this->fullSkriptName.'/'.$projectName.'"> </a>'; + } + } + elseif( $this->requestType == 'object' ) // Verzeichnisinhalt + { + $objects = $this->folder->getObjects(); + + foreach( $objects as $object ) + { + printf($format, + number_format(1), + strftime("%Y-%m-%d %H:%M:%S",$object->lastchangeDate ), + '<a href="'.$object->filename.'">'.$object->filename.'</a>'); + echo $nl; + } + } + + echo '</pre>'; + echo '</body>'; + echo '</html>'; + } + + + + /** + * Die Methode LOCK sollte garnicht aufgerufen werden, da wir nur + * Dav-Level 1 implementieren und dies dem Client auch mitteilen.<br> + * <br> + * Ausgabe von HTTP-Status 412 (Precondition failed) + */ + function davLOCK() + { + $this->httpStatus('412 Precondition failed'); + $this->davOPTIONS(); + } + + + + /** + * Die Methode UNLOCK sollte garnicht aufgerufen werden, da wir nur + * Dav-Level 1 implementieren und dies dem Client auch mitteilen.<br> + * <br> + * Ausgabe von HTTP-Status 412 (Precondition failed) + */ + public function davUNLOCK() + { + $this->httpStatus('412 Precondition failed'); + $this->davOPTIONS(); + } + + + + /** + * Die Methode POST ist bei WebDav nicht sinnvoll.<br> + * <br> + * Ausgabe von HTTP-Status 405 (Method Not Allowed) + */ + public function davPOST() + { + // Die Methode POST ist bei Webdav nicht sinnvoll. + $this->httpStatus('405 Method Not Allowed' ); + } + + + + /** + * Verzeichnis anlegen. + */ + public function davMKCOL() + { + + if ( !empty($this->request) ) + { + $this->httpStatus('415 Unsupported Media Type' ); // Kein Body erlaubt + } + elseif ( $this->readonly ) + { + $this->httpStatus('403 Forbidden' ); // Kein Schreibzugriff erlaubt + } + elseif ( !$this->folder->hasRight( ACL_CREATE_FOLDER ) ) + { + $this->httpStatus('403 Forbidden' ); // Benutzer darf das nicht + } + elseif ( $this->obj == null ) + { + // Die URI ist noch nicht vorhanden + $f = new Folder(); + $f->filename = basename($this->fullSkriptName); + $f->parentid = $this->folder->objectid; + $f->projectid = $this->project->projectid; + $f->add(); + $this->httpStatus('201 Created'); + } + else + { + // MKCOL ist nicht moeglich, wenn die URI schon existiert. + Logger::warn('MKCOL-Request to an existing resource'); + $this->httpStatus('405 Method Not Allowed' ); + } + } + + + + /** + * Objekt l�schen. + */ + public function davDELETE() + { + if ( $this->readonly ) + { + $this->httpStatus('403 Forbidden' ); // Kein Schreibzugriff erlaubt + } + else + { + if ( $this->obj == null ) + { + // Nicht existente URIs kann man auch nicht loeschen. + $this->httpStatus('404 Not Found' ); + } + elseif ( ! $this->obj->hasRight( ACL_DELETE ) ) + { + $this->httpStatus('403 Forbidden' ); // Benutzer darf die Resource nicht loeschen + } + elseif ( $this->obj->isFolder ) + { + $f = new Folder( $this->obj->objectid ); + $f->deleteAll(); + $this->httpStatus( true ); // OK + Logger::debug('Deleted folder with id '.$this->obj->objectid ); + } + elseif ( $this->obj->isFile ) + { + $f = new File( $this->obj->objectid ); + $f->delete(); + $this->httpStatus( true ); // OK + } + elseif ( $this->obj->isPage ) + { + $p = new Page( $this->obj->objectid ); + $p->delete(); + $this->httpStatus( true ); // OK + } + elseif ( $this->obj->isLink ) + { + $l = new Link( $this->obj->objectid ); + $l->delete(); + $this->httpStatus( true ); // OK + } + + } + } + + + + /** + * Kopieren eines Objektes.<br> + * Momentan ist nur das Kopieren einer Datei implementiert.<br> + * Das Kopieren von Ordnern, Verkn�pfungen und Seiten ist nicht moeglich. + */ + public function davCOPY() + { + if ( $this->readonly || !$this->create ) + { + error_log('WEBDAV: COPY request, but readonly or no creating'); + $this->httpStatus('405 Not Allowed' ); + } + elseif( $this->obj == null ) + { + // Was nicht da ist, laesst sich auch nicht verschieben. + error_log('WEBDAV: COPY request, but Source not found'); + $this->httpStatus('405 Not Allowed' ); + } + elseif ( $this->destination == null ) + { + error_log('WEBDAV: COPY request, but no "Destination:"-Header'); + // $this->httpStatus('405 Not Allowed' ); + $this->httpStatus('412 Precondition failed'); + } + else + { + // URL parsen. + $dest = $this->destination; + $destinationProject = $dest['project']; + $destinationFolder = $dest['folder' ]; + $destinationObject = $dest['object' ]; + + if ( $dest['type'] != 'object' ) + { + Logger::debug('WEBDAV: COPY request, but "Destination:"-Header mismatch'); + $this->httpStatus('405 Not Allowed'); + } + elseif ( $this->project->projectid != $destinationProject->projectid ) + { + // Kopieren in anderes Projekt nicht moeglich. + Logger::debug('WEBDAV: COPY request denied, project does not match'); + $this->httpStatus('403 Forbidden'); + } + elseif ( $destinationObject != null ) + { + Logger::debug('WEBDAV: COPY request denied, Destination exists. Overwriting is not supported'); + $this->httpStatus('403 Forbidden'); + } + elseif ( is_object($destinationFolder) && ! $destinationFolder->hasRight( ACL_CREATE_FILE ) ) + { + $this->httpStatus('403 Forbidden' ); // Benutzer darf das nicht + } + elseif ( is_object($destinationObject) && $destinationObject->isFolder) + { + Logger::debug('WEBDAV: COPY request denied, Folder-Copy not implemented'); + $this->httpStatus('405 Not Allowed'); + } + elseif ( is_object($destinationObject) && $destinationObject->isLink) + { + Logger::debug('WEBDAV: COPY request denied, Link copy not implemented'); + $this->httpStatus('405 Not Allowed'); + } + elseif ( is_object($destinationObject) && $destinationObject->isPage) + { + Logger::debug('WEBDAV: COPY request denied, Page copy not implemented'); + $this->httpStatus('405 Not Allowed'); + } + else + { + $f = new File(); + $f->filename = basename($_SERVER['HTTP_DESTINATION']); + $f->name = ''; + $f->parentid = $destinationFolder->objectid; + $f->projectid = $this->project->projectid; + $f->add(); + $f->copyValueFromFile( $this->obj->objectid ); + + Logger::debug('WEBDAV: COPY request accepted' ); + // Objekt wird in anderen Ordner kopiert. + $this->httpStatus('201 Created' ); + } + } + + } + + + + /** + * Verschieben eines Objektes.<br> + * <br> + * Folgende Operationen sind m�glich:<br> + * - Unbenennen eines Objektes (alle Typen)<br> + * - Verschieben eines Objektes (alle Typen) in einen anderen Ordner.<br> + */ + public function davMOVE() + { + if ( $this->readonly ) + { + $this->httpStatus('403 Forbidden - Readonly Mode' ); // Schreibgeschuetzt + } + elseif ( !$this->create ) + { + $this->httpStatus('403 Forbidden - No creation' ); // Schreibgeschuetzt + } + elseif( $this->obj == null ) + { + // Was nicht da ist, laesst sich auch nicht verschieben. + $this->httpStatus('404 Not Found' ); + } + elseif( is_object($this->obj) && ! $this->obj->hasRight( ACL_WRITE ) ) + { + // Was nicht da ist, laesst sich auch nicht verschieben. + Logger::error('Source '.$this->obj->objectid.' is not writable: Forbidden'); + $this->httpStatus('403 Forbidden' ); + } + elseif ( $this->destination == null ) + { + Logger::error('WEBDAV: MOVE request, but no "Destination:"-Header'); + // $this->httpStatus('405 Not Allowed' ); + $this->httpStatus('412 Precondition failed'); + } + else + { + $dest = $this->destination; + $destinationProject = $dest['project']; + $destinationFolder = $dest['folder' ]; + $destinationObject = $dest['object' ]; + + if ( $dest['type'] != 'object' ) + { + Logger::debug('WEBDAV: MOVE request, but "Destination:"-Header mismatch'); + $this->httpStatus('405 Not Allowed'); + return; + } + + if ( is_object($destinationFolder) && ! $destinationFolder->hasRight( ACL_CREATE_FILE ) ) + { + Logger::error('Source '.$this->obj->objectid.' is not writable: Forbidden'); + $this->httpStatus('403 Forbidden' ); + } + + if ( $destinationObject != null ) + { + Logger::debug('WEBDAV: MOVE request denied, destination exists'); + $this->httpStatus('412 Precondition Failed'); + return; + } + + if ( $this->project->projectid != $destinationProject->projectid ) + { + // Verschieben in anderes Projekt nicht moeglich. + Logger::debug('WEBDAV: MOVE request denied, project does not match'); + $this->httpStatus('405 Not Allowed'); + return; + } + + if ( $this->folder->objectid == $destinationFolder->objectid ) + { + Logger::debug('WEBDAV: MOVE request accepted, object renamed'); + // Resource bleibt in gleichem Ordner. + $this->obj->filename = basename($_SERVER['HTTP_DESTINATION']); + $this->obj->objectSave(false); + $this->httpStatus('201 Created' ); + return; + } + + if ( $destinationFolder->isFolder ) + { + Logger::debug('WEBDAV: MOVE request accepted, Destination: '.$destinationFolder->filename ); + // Objekt wird in anderen Ordner verschoben. + $this->obj->setParentId( $destinationFolder->objectid ); + $this->httpStatus('201 Created' ); + return; + } + + Logger::warn('WEBDAV: MOVE request failed' ); + $this->httpStatus('500 Internal Server Error' ); + } + } + + + + /** + * Anlegen oder �berschreiben Dateien �ber PUT.<br> + * Dateien k�nnen neu angelegt und �berschrieben werden.<br> + * <br> + * Seiten k�nnen nicht �berschrieben werden. Wird versucht, + * eine Seite mit PUT zu �berschreiben, wird der Status "405 Not Allowed" gemeldet.<br> + */ + public function davPUT() + { + // TODO: 409 (Conflict) wenn �bergeordneter Ordner nicht da. + + if ( $config['readonly'] ) + { + $this->httpStatus('405 Not Allowed' ); + } + elseif ( strlen($this->request) > $this->maxFileSize*1000 ) + { + // Maximale Dateigroesse ueberschritten. + // Der Status 207 "Zuwenig Speicherplatz" passt nicht ganz, aber fast :) + $this->httpStatus('507 Insufficient Storage' ); + } + elseif ( $this->obj == null ) + { + // Neue Datei anlegen + if ( !$config['create'] ) + { + Logger::warn('WEBDAV: Creation of files not allowed by configuration' ); + $this->httpStatus('405 Not Allowed' ); + } + + if ( ! $this->folder->hasRight( ACL_CREATE_FILE ) ) + { + $this->httpStatus('403 Forbidden'); + return; + } + + $file = new File(); + $file->filename = basename($this->fullSkriptName); + $file->extension = ''; + $file->size = strlen($this->request); + $file->parentid = $this->folder->objectid; + $file->projectid = $this->project->projectid; + $file->value = $this->request; + $file->add(); + $this->httpStatus('201 Created'); + return; + } + elseif ( $this->obj->isFile ) + { + if ( ! $this->obj->hasRight( ACL_WRITE ) ) + { + Logger::debug('PUT failed, parent folder not writable by user' ); + $this->httpStatus('403 Forbidden'); + return; + } + + // Bestehende Datei ueberschreiben. + $file = new File( $this->obj->objectid ); + $file->saveValue( $this->request ); + $file->setTimestamp(); + $this->httpStatus('204 No Content'); + Logger::debug('PUT ok, file is created' ); + return; + } + elseif ( $this->obj->isFolder ) + { + Logger::error('PUT on folder is not supported, use PROPFIND. Lame client?' ); + $this->httpStatus('405 Not Allowed' ); + } + else + { + // Fuer andere Objekttypen (Links, Seiten) ist kein PUT moeglich. + Logger::warn('PUT only available for files, pages and links are ignored' ); + $this->httpStatus('405 Not Allowed' ); + } + } + + + + /** + * WebDav-Methode PROPFIND. + * + * Diese Methode wird + * - beim Ermitteln von Verzeichnisinhalten und + * - beim Ermitteln von Metainformationen zu einer Datei + * verwendet. + * + * Das Ergebnis wird in einer XML-Zeichenkette geliefert. + */ + public function davPROPFIND() + { + switch( $this->requestType ) + { + case 'projectlist': // Projektliste + + $inhalte = array(); + + $objektinhalt = array(); + $z = 30*365.25*24*60*60; + $objektinhalt['createdate' ] = $z; + $objektinhalt['lastchangedate'] = $z; + $objektinhalt['size' ] = 1; + $objektinhalt['name' ] = $this->fullSkriptName; + $objektinhalt['displayname' ] = ''; + $objektinhalt['type'] = 'folder'; + + $inhalte[] = $objektinhalt; + + foreach( Project::getAll() as $projectid=>$projectName ) + { + $project = new Project( $projectid ); + $rootObjectId = $project->getRootObjectId(); + $folder = new Folder( $rootObjectId ); + $folder->load(); + + $objektinhalt = array(); + $z = 30*365.25*24*60*60; + $objektinhalt['createdate' ] = $z; + $objektinhalt['lastchangedate'] = $folder->lastchangeDate; + $objektinhalt['size' ] = $project->size(); + $objektinhalt['name' ] = $this->fullSkriptName.$projectName.'/'; + $objektinhalt['displayname' ] = $projectName; + $objektinhalt['type'] = 'folder'; + $inhalte[] = $objektinhalt; + } + + $this->multiStatus( $inhalte ); + break; + + case 'object': // Verzeichnisinhalt + + if ( $this->obj == null ) + { + // Objekt existiert nicht. + Logger::trace( 'WEBDAV: PROPFIND of non-existent object'); + $this->httpStatus('404 Not Found'); + return; + } + elseif ( $this->obj->isFolder ) + { + if ( ! $this->obj->hasRight( ACL_READ )) + { + Logger::debug( 'Folder '.$this->obj->objectid.': access denied'); + $this->httpStatus('403 Forbidden'); + } + + $inhalte = array(); + + $objektinhalt = array(); + $objektinhalt['createdate' ] = $this->obj->createDate; + $objektinhalt['lastchangedate'] = $this->obj->lastchangeDate; + $objektinhalt['name' ] = $this->fullSkriptName; + $objektinhalt['displayname' ] = basename($this->fullSkriptName); + $objektinhalt['type' ] = 'folder'; + $objektinhalt['size' ] = 0; + $inhalte[] = $objektinhalt; + + if ( $this->depth > 0 ) + { + $objects = $this->folder->getObjects(); + foreach( $objects as $object ) + { + if ( ! $object->hasRight( ACL_READ )) + continue; + + //$object->loadRaw(); + $objektinhalt = array(); + $objektinhalt['createdate' ] = $object->createDate; + $objektinhalt['lastchangedate'] = $object->lastchangeDate; + $objektinhalt['displayname' ] = $object->filename; + + switch( $object->getType() ) + { + + case OR_TYPE_FOLDER: + $objektinhalt['name'] = $this->fullSkriptName.$object->filename.'/'; + $objektinhalt['type'] = 'folder'; + $objektinhalt['size'] = 0; + $inhalte[] = $objektinhalt; + break; + case OR_TYPE_FILE: + $objektinhalt['name'] = $this->fullSkriptName.$object->filename; + $objektinhalt['type'] = 'file'; + $file = new File($object->objectid); + $file->load(); + $objektinhalt['size'] = $file->size; + $objektinhalt['mime'] = 'application/x-non-readable'; + $inhalte[] = $objektinhalt; + break; + case OR_TYPE_LINK: + $objektinhalt['name'] = $this->fullSkriptName.$object->filename; + $objektinhalt['type'] = 'file'; + $objektinhalt['size'] = 0; + $objektinhalt['mime'] = 'application/x-non-readable'; + $inhalte[] = $objektinhalt; + break; + case OR_TYPE_PAGE: + $objektinhalt['name'] = $this->fullSkriptName.$object->filename; + $objektinhalt['type'] = 'file'; + $objektinhalt['size'] = 0; + $inhalte[] = $objektinhalt; + break; + default: + } + } + } + Logger::trace( 'WEBDAV: PROPFIND-2'); + +// if ( count($inhalte)==0 ) +// $inhalte[] = array('createdate'=>0,'lastchangedate'=>0,'name'=>'empty','size'=>0,'type'=>'file'); + + Logger::trace('Anzahl Dateien:'.count($inhalte)); + $this->multiStatus( $inhalte ); + } + else + { + $object = $this->obj; + Logger::trace( 'WEBDAV: PROPFIND of file'); + $objektinhalt = array(); + $objektinhalt = array(); + $objektinhalt['name'] = $this->fullSkriptName.'/'.$object->filename.'/'; + $objektinhalt['displayname'] = $object->filename; + $objektinhalt['createdate' ] = $object->createDate; + $objektinhalt['lastchangedate'] = $object->lastchangeDate; + $file = new File( $this->obj->objectid ); + $file->load(); + $objektinhalt['size' ] = $file->size; + $objektinhalt['type' ] = 'file'; + + + $this->multiStatus( array($objektinhalt) ); + } + break; + + default: + Logger::warn('Internal Error, unknown request type: '. $this->requestType); + $this->httpStatus('500 Internal Server Error'); + } + } + + + /** + * Webdav-Methode PROPPATCH ist nicht implementiert. + */ + public function davPROPPATCH() + { + // TODO: Multistatus erzeugen. + // Evtl. ist '409 Conflict' besser? + $this->httpStatus('405 Not Allowed'); + } + + + /** + * Erzeugt einen Multi-Status. + * @access private + */ + private function multiStatus( $files ) + { + $this->httpStatus('207 Multi-Status'); + header('Content-Type: text/xml; charset=utf-8'); + + $response = ''; + $response .= '<?xml version="1.0" encoding="utf-8" ?>'; + $response .= '<d:multistatus xmlns:d="DAV:">'; + + foreach( $files as $file ) + $response .= $this->getResponse( $file['name'],$file ); + + $response .= '</d:multistatus>'; + Logger::trace('PROPFIND: '.$response); + + $response = utf8_encode($response); + + header('Content-Length: '.strlen($response)); + echo $response; + } + + + /** + * Erzeugt ein "response"-Element, welches in ein "multistatus"-element verwendet werden kann. + */ + private function getResponse( $file,$options ) + { + // TODO: Nur angeforderte Elemente erzeugen. + $response = ''; + $response .= '<d:response>'; + $response .= '<d:href>'.$file.'</d:href>'; + $response .= '<d:propstat>'; + $response .= '<d:prop>'; + // $response .= '<d:source></d:source>'; + $response .= '<d:creationdate>'.date('r',$options['createdate']).'</d:creationdate>'; + $response .= '<d:displayname>'.$options['displayname'].'</d:displayname>'; + $response .= '<d:getcontentlength>'.$options['size'].'</d:getcontentlength>'; + $response .= '<d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">'.date('r',$options['lastchangedate']).'</d:getlastmodified>'; + + if ( $options['type'] == 'folder') + $response .= '<d:resourcetype><d:collection/></d:resourcetype>'; + else + $response .= '<d:resourcetype />'; + + $response .= '<d:categories />'; + $response .= '<d:fields></d:fields>'; + + + +// $response .= '<d:getcontenttype>text/html</d:getcontenttype>'; +// $response .= '<d:getcontentlength />'; +// $response .= '<d:getcontentlanguage />'; +// $response .= '<d:executable />'; +// $response .= '<d:resourcetype>'; +// $response .= '<d:collection />'; +// $response .= '</d:resourcetype>'; +// $response .= '<d:getetag />'; + + $response .= '</d:prop>'; + $response .= '<d:status>HTTP/1.1 200 OK</d:status>'; + $response .= '</d:propstat>'; + $response .= '</d:response>'; + + return $response; + } + + + + /** + * URI parsen. + */ + private function parseURI( $uri ) + { + // Ergebnis initialisieren (damit alle Schl�ssel vorhanden sind) + $ergebnis = array('type' => null, + 'project' => null, + 'path' => array(), + 'folder' => null, + 'object' => null ); + +// Logger::trace( 'WEBDAV: Parsen der URI '.$uri); + $uriParts = explode('/',$uri); + + $nr = 0; + $f = null; + $o = null; + $ergebnis['type'] = 'projectlist'; + + foreach( $uriParts as $uriPart ) + { + if ( empty( $uriPart)) + continue; + + $ergebnis['path'][] = $uriPart; + + if ( $f == null ) + { + // URI='/project/' + // Name des Projektes in der URL, es wird das Projekt geladen. + $ergebnis['type'] = 'object'; + + $p = new Project(); + $p->name = $uriPart; +// Logger::trace("Projektname: ".$p->name); + $p->loadByName(); + $ergebnis['project'] = $p; + // Das Projekt hat weder Sprache noch Variante gesetzt. + //Session::setProjectLanguage( new Language( $this->project->getDefaultLanguageId() ) ); + //Session::setProjectModel ( new Model ( $this->project->getDefaultModelId() ) ); + + $oid = $p->getRootObjectId(); + + $f = new Folder($oid); + $ergebnis['object'] = $f; + $ergebnis['folder'] = $f; + + } + else + { + if ( $ergebnis['object'] == null ) + { + $this->httpStatus('409 Conflict'); + exit; + } + + $oid = $f->getObjectIdByFileName($uriPart); + + if ( $oid == 0 ) + { + $ergebnis['object'] = null; + } + else + { + $o = new Object($oid); + $o->load(); + $ergebnis['object'] = $o; + + if ( $o->isFolder ) + { + $f = new Folder($oid); + $ergebnis['folder'] = $f; + } + } + } + } + + return $ergebnis; + } +} + + + +/** + * Fehler-Handler fuer WEBDAV.<br> + * Bei einem Laufzeitfehler ist eine Ausgabe des Fehlers auf der Standardausgabe sinnlos, + * da der WebDAV-Client dies nicht lesen oder erkennen kann. + * Daher wird der Fehler-Handler umgebogen, so dass nur ein Logeintrag sowie ein + * Server-Fehler erzeugt wird. + */ +function webdavErrorHandler($errno, $errstr, $errfile, $errline) +{ + error_log('WEBDAV ERROR: '.$errno.'/'.$errstr.'/file:'.$errfile.'/line:'.$errline); + + // Wir teilen dem Client mit, dass auf dem Server was schief gelaufen ist. + WebDAV::httpStatus('500 Internal Server Error, WebDAV-Request failed with "'.$errstr.'"'); + exit; +} + + + +/** + * Setzt einen HTTP-Status.<br> + * <br> + * Es wird ein HTTP-Status gesetzt, zus�tzlich wird der Status in den Header "X-WebDAV-Status" geschrieben.<br> + * Ist der Status nicht 200 oder 207 (hier folgt ein BODY), wird das Skript beendet. + */ +function httpStatus( $status = true ) +{ + if ( $status === true ) + $status = '200 OK'; + + // Logger::debug('WEBDAV: HTTP-Status: '.$status); + + header('HTTP/1.1 '.$status); + header('X-WebDAV-Status: '.$status,true); + + // RFC 2616 (HTTP/1.1), Section 10.4.6 "405 Method Not Allowed" says: + // "[...] The response MUST include an + // Allow header containing a list of valid methods for the requested + // resource." + // + // RFC 2616 (HTTP/1.1), Section 14.7 "Allow" says: + // "[...] An Allow header field MUST be + // present in a 405 (Method Not Allowed) response." + if ( substr($status,0,3) == '405' ) + header('Allow: '.implode(', ',$this->allowed_methods()) ); +} + +?>+ \ No newline at end of file diff --git a/dav/index.php b/dav/index.php @@ -0,0 +1 @@ +<?php require('dav.php') ?>+ \ No newline at end of file