eZ Publish  [4.0]
ezwebdavserver.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZWebDAVServer class
00004 //
00005 // Created on: <01-Aug-2003 13:13:13 bh>
00006 //
00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00008 // SOFTWARE NAME: eZ Publish
00009 // SOFTWARE RELEASE: 4.0.x
00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS
00011 // SOFTWARE LICENSE: GNU General Public License v2.0
00012 // NOTICE: >
00013 //   This program is free software; you can redistribute it and/or
00014 //   modify it under the terms of version 2.0  of the GNU General
00015 //   Public License as published by the Free Software Foundation.
00016 //
00017 //   This program is distributed in the hope that it will be useful,
00018 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //   GNU General Public License for more details.
00021 //
00022 //   You should have received a copy of version 2.0 of the GNU General
00023 //   Public License along with this program; if not, write to the Free
00024 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 //   MA 02110-1301, USA.
00026 //
00027 //
00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00029 //
00030 
00031 /*! \file ezwebdavserver.php
00032  WebDAV server base class.
00033 */
00034 
00035 /*! \defgroup eZWebDAV WebDAV system */
00036 
00037 /*!
00038   \class eZWebDAVServer ezwebdavserver.php
00039   \ingroup eZWebDAV
00040   \brief Virtual base class for implementing WebDAV servers.
00041 
00042   \todo Add support for propall and propname
00043 */
00044 
00045 //include_once( "lib/ezutils/classes/ezmimetype.php" );
00046 //include_once( 'lib/ezfile/classes/ezdir.php' );
00047 
00048 /*!
00049   \return \c true if logging is enabled.
00050   \deprecated Use eZWebDAVServer::isLoggingEnabled() instead.
00051 */
00052 function eZWebDavCheckLogSetting()
00053 {
00054     return eZWebDAVServer::isLoggingEnabled();
00055 }
00056 
00057 /*!
00058   Logs the string \a $logString to the logfile /tmp/webdavlog.txt
00059   if logging is enabled.
00060   \deprecated Appending to log file is now done through appendLogEntry in the webdav server class.
00061 */
00062 function append_to_log( $logString )
00063 {
00064     return eZWebDAVServer::appendLogEntry( $logString );
00065 }
00066 
00067 /*!
00068   Logs the string \a $logString to the logfile /tmp/webdavlog.txt
00069   if logging is enabled.
00070   \deprecated Appending to log file is now done through appendLogEntry in the webdav server class.
00071 */
00072 function eZWebDavAppendToLog( $logString )
00073 {
00074     return eZWebDAVServer::appendLogEntry( $logString );
00075 }
00076 
00077 class eZWebDAVServer
00078 {
00079     // General status OK return codes:
00080     const OK = 10;
00081     const OK_SILENT = 11;
00082     const OK_CREATED = 12;
00083     const OK_OVERWRITE = 13;
00084 
00085     // General status FAILED return codes:
00086     const FAILED_FORBIDDEN = 30;
00087     const FAILED_NOT_FOUND = 31;
00088     const FAILED_EXISTS = 32;
00089     const FAILED_CONFLICT = 33;
00090     const FAILED_PRECONDITION = 34;
00091     const FAILED_LOCKED = 35;
00092     const FAILED_BAD_GATEWAY = 36;
00093     const FAILED_STORAGE_FULL = 37;
00094     const FAILED_UNSUPPORTED = 38;
00095 
00096     // File timestamp formats (MUST be correct, or else: Won't work in MSIE...).
00097     // Yes, the two timestamps are actually in different formats. Don't touch!
00098     const CTIME_FORMAT = "Y-m-d\\TH:i:s\\Z";
00099     const MTIME_FORMAT = "D, d M Y H:i:s";
00100 
00101     // Temporary (uploaded) file stuff:
00102     const TEMP_FILE_PREFIX = "eZWebDAVUpload_";
00103 
00104     /*! Constructor of eZWebDAVServer;
00105         disables PHP error messages.
00106      */
00107     function eZWebDAVServer()
00108     {
00109         $this->setupXMLOutputCharset();
00110     }
00111 
00112     function setServerRoot( $rootDir )
00113     {
00114         if ( file_exists ( $rootDir ) )
00115         {
00116             $this->ServerRootDir = $rootDir;
00117 
00118             return true;
00119         }
00120         else
00121         {
00122             return false;
00123         }
00124     }
00125 
00126     /*! Server process function.
00127         Dumps a custom header, sets the path and finally
00128         checks what the clients wants. Calls the appropriate
00129         virtual function (based on the client request).
00130      */
00131     function processClientRequest()
00132     {
00133         $this->appendLogEntry( "WebDAV server started...", 'processClientRequest' );
00134         $this->XMLBodyRead = false;
00135 
00136         // Dump some custom header/info.
00137         $this->headers();
00138 
00139         // Clear file status cache (just in case).
00140         clearstatcache();
00141 
00142         // Convert the requested URI string to non-bogus format.
00143         $target = urldecode( $_SERVER["REQUEST_URI"] );
00144         $target = $this->processURL( $target );
00145 
00146         $this->appendLogEntry( "----------------------------------------" );
00147         $this->appendLogEntry( "Client says: " . $_SERVER["REQUEST_METHOD"], 'processClientRequest' );
00148         $this->appendLogEntry( "Target: " . $_SERVER["REQUEST_URI"], 'processClientRequest' );
00149         $this->appendLogEntry( "----------------------------------------" );
00150 
00151         $status = eZWebDAVServer::FAILED_NOT_FOUND;
00152 
00153         switch ( $_SERVER["REQUEST_METHOD"] )
00154         {
00155             // OPTIONS  (Server ID reply.)
00156             case "OPTIONS":
00157             {
00158                 $this->appendLogEntry( "OPTIONS was issued from client.", 'processClientRequest' );
00159                 $options = $this->options( $target );
00160                 $status  = $this->outputOptions( $options );
00161             } break;
00162 
00163             // PROPFIND (Show dir/collection content.)
00164             case "PROPFIND":
00165             {
00166                 $this->appendLogEntry( "PROPFIND was issued from client.", 'processClientRequest' );
00167 
00168                 $depth = $_SERVER['HTTP_DEPTH'];
00169                 if ( $depth != 0 and $depth != 1 and $depth != "infinity" )
00170                     $depth = "infinity";
00171                 $this->appendLogEntry( "Depth: $depth.", 'processClientRequest' );
00172 
00173                 $xmlBody = $this->xmlBody();
00174                 // Find which properties were requested
00175                 // $this->appendLogEntry( $xmlBody, 'xmlbody' );
00176                 $dom = new DOMDocument( '1.0', 'utf-8' );
00177                 $dom->preserveWhiteSpace = false;
00178                 $ok = $dom->loadXML( $xmlBody );
00179 
00180                 $requestedProperties = array();
00181                 if ( $ok )
00182                 {
00183                     $propfindNode = $dom->documentElement;
00184                     $propNode = $propfindNode->getElementsByTagName( 'prop' )->item( 0 );
00185                     if ( $propNode )
00186                     {
00187                         $propList = $propNode->childNodes;
00188                         foreach ( $propList as $node )
00189                         {
00190                             $name = $node->localName;
00191                             $requestedProperties[] = $name;
00192                         }
00193                     }
00194                     else
00195                     {
00196                         $allpropNode = $propfindNode->getElementsByTagName( 'allprop' )->item( 0 );
00197                         if ( $allpropNode )
00198                         {
00199                             // The server must return all possible properties
00200                             $requestedProperties = true;
00201                         }
00202                         else
00203                         {
00204                             $propnameNode = $propfindNode->getElementsByTagName( 'propname' )->item( 0 );
00205                             if ( $propnameNode )
00206                             {
00207                                 // The server must return only the names of all properties
00208                                 $requestedProperties = false;
00209                             }
00210                         }
00211                     }
00212                 }
00213 
00214                 $collection = $this->getCollectionContent( $target, $depth, $requestedProperties );
00215                 if ( is_array( $collection ) )
00216                 {
00217                     $status = $this->outputCollectionContent( $collection, $requestedProperties );
00218                 }
00219                 else
00220                 {
00221                     $status = $collection;
00222                 }
00223             } break;
00224 
00225             // HEAD (Check if file/resource exists.)
00226             case "HEAD":
00227             {
00228                 $this->appendLogEntry( "HEAD was issued from client.", 'processClientRequest' );
00229                 $data   = $this->get( $target );
00230                 $status = $this->outputSendDataToClient( $data, true );
00231             } break;
00232 
00233             // GET (Download a file/resource from server to client.)
00234             case "GET":
00235             {
00236                 $this->appendLogEntry( "GET was issued from client.", 'processClientRequest' );
00237                 $data   = $this->get( $target );
00238                 $status = $this->outputSendDataToClient( $data );
00239             } break;
00240 
00241             // PUT (Upload a file/resource from client to server.)
00242             case "PUT":
00243             {
00244                 $this->appendLogEntry( "PUT was issued from client.", 'processClientRequest' );
00245                 $status = eZWebDAVServer::OK_CREATED;
00246 
00247                 // Attempt to get file/resource sent from client/browser.
00248                 $tempFile = $this->storeUploadedFile( $target );
00249 
00250                 // If there was an actual file:
00251                 if ( $tempFile )
00252                 {
00253                     // Attempt to do something with it (copy/whatever).
00254                     $status = $this->put( $target, $tempFile );
00255 
00256                     unlink( $tempFile );
00257                     //include_once( 'lib/ezfile/classes/ezdir.php' );
00258                     eZDir::cleanupEmptyDirectories( dirname( $tempFile ) );
00259                 }
00260                 // Else: something went wrong...
00261                 else
00262                 {
00263                     $status = eZWebDAVServer::FAILED_FORBIDDEN;
00264                 }
00265 
00266             } break;
00267 
00268             // MKCOL (Create a directory/collection.)
00269             case "MKCOL":
00270             {
00271                 $this->appendLogEntry( "MKCOL was issued from client.", 'processClientRequest' );
00272                 if ( strlen( $this->xmlBody() ) > 0 )
00273                 {
00274                     $this->appendLogEntry( "MKCOL body error.", 'processClientRequest' );
00275                     $status = eZWebDAVServer::FAILED_FORBIDDEN;
00276                 }
00277                 else
00278                 {
00279                     $status = $this->mkcol( $target );
00280                 }
00281             } break;
00282 
00283             // COPY (Copy a resource/collection from one location to another.)
00284             case "COPY":
00285             {
00286                 $this->appendLogEntry( "COPY was issued from client.", 'processClientRequest' );
00287 
00288                 $source      = $target;
00289                 $url         = parse_url( $_SERVER["HTTP_DESTINATION"] );
00290                 $destination = urldecode( $url["path"] );
00291                 $destination = $this->processURL( $destination );
00292                 $status      = $this->copy( $source, $destination );
00293             } break;
00294 
00295             // MOVE (Move a resource/collection from one location to another.)
00296             case "MOVE":
00297             {
00298                 $this->appendLogEntry( "MOVE was issued from client.", 'processClientRequest' );
00299                 $source      = $target;
00300                 $url         = parse_url( $_SERVER["HTTP_DESTINATION"] );
00301                 $destination = urldecode( $url["path"] );
00302                 $destination = $this->processURL( $destination );
00303                 $status      = $this->move( $source, $destination );
00304             } break;
00305 
00306             // DELETE (Remove a resource/collection.)
00307             case "DELETE":
00308             {
00309                 $this->appendLogEntry( "DELETE was issued from client.", 'processClientRequest' );
00310                 $status = $this->delete( $target );
00311             } break;
00312 
00313             // Default case: unknown command from client.
00314             default:
00315             {
00316                 // __FIX_ME__
00317             } break;
00318         }
00319         // Read the XML body if it is not used yet,
00320         // PHP should discard it but it's a bug in some PHP versions
00321         if ( !$this->XMLBodyRead )
00322             $this->flushXMLBody();
00323 
00324         // Handle the returned status code (post necessary/matching headers, etc.).
00325         $this->handle( $status );
00326     }
00327 
00328     /*!
00329       \protected
00330       Generates HTTP headers with information on what the server supports.
00331       \param $options An array with the various options the server supports
00332                       - methods - An array with methods it can handle,
00333                                    if not supplied it will report all possible methods.
00334                       - versions - An array with versions this server supports,
00335                                     if not supplied it will return 1,2,<http://apache.org/dav/propset/fs/1>.
00336       \return The WebDAV status code
00337     */
00338     function outputOptions( $options )
00339     {
00340         // Default options
00341         $methods = array( 'OPTIONS', 'GET', 'HEAD', 'POST', 'DELETE', 'TRACE', 'PROPFIND', 'PROPPATCH', 'COPY', 'MOVE', 'LOCK', 'UNLOCK' );
00342         $versions = array( '1', '2', '<http://apache.org/dav/propset/fs/1>' );
00343 
00344         if ( isset( $options['methods'] ) )
00345             $methods = $options['methods'];
00346         if ( isset( $options['versions'] ) )
00347             $versions = $options['versions'];
00348 
00349         header( 'Content-Length: 0' );
00350         header( 'MS-Author-Via: DAV' );
00351         header( 'Allow: ' . implode( ', ', $methods ) );
00352         header( 'DAV: ' . implode( ',', $versions ) );
00353         header( 'Content-Type: text/plain; charset=iso-8859-1' );
00354 
00355         return eZWebDAVServer::OK_SILENT;
00356     }
00357 
00358     /*!
00359       \protected
00360       Generates the WebDAV XML from \a $collection and outputs using print().
00361       \param $collection An array with elements (e.g dirs/files).
00362                          Each element consists of:
00363                          - ctime - The timestamp when the element was created
00364                          - mtime - The timestamp when the element was last modified
00365                          - mimetype - The type of element, use httpd/unix-directory for folder like entries
00366                          - href - URL which points to the element
00367                          - name - The name of the element
00368                          - size - The size of the element in bytes, not needed for folders
00369       \return The WebDAV status code
00370     */
00371     function outputCollectionContent( $collection, $requestedProperties )
00372     {
00373         // Sanity check & action: if client forgot to ask, we'll still
00374         // play along revealing some basic/default properties. This is
00375         // necessary to make it work with Windows XP + SP2.
00376         if( !is_array( $requestedProperties ) || count( $requestedProperties ) == 0 )
00377         {
00378             $requestedProperties = array( 'displayname',
00379                                           'creationdate',
00380                                           'getlastmodified',
00381                                           'getcontenttype',
00382                                           'getcontentlength',
00383                                           'resourcetype' );
00384         }
00385 
00386         // Fix for CaDAVer (text based linux client) -> error if not revealed.
00387         // Apparently it does not mess up for other clients so I'll just leave
00388         // it without wrapping it inside a client-specific check.
00389         if( !in_array( 'getcontenttype', $requestedProperties ) )
00390         {
00391             $requestedProperties[] = 'getcontenttype';
00392         }
00393 
00394         $this->appendLogEntry( 'Client requested ' .
00395                                count( $requestedProperties ) .
00396                                ' properties.', 'outputCollectionContent' );
00397 
00398         $dataCharset = eZWebDAVServer::dataCharset();
00399         $xmlCharset = $this->XMLOutputCharset();
00400 
00401         $xmlText = "<?xml version=\"1.0\" encoding=\"$xmlCharset\"?>\n" .
00402                    "<D:multistatus xmlns:D=\"DAV:\">\n";
00403 
00404         // Maps from WebDAV property names to internal names
00405         $nameMap = array( 'displayname' => 'name',
00406                           'creationdate' => 'ctime',
00407                           'getlastmodified' => 'mtime',
00408                           'getcontenttype' => 'mimetype',
00409                           'getcontentlength' => 'size' );
00410 
00411         foreach ( $requestedProperties as $requestedProperty )
00412         {
00413             if ( !isset( $nameMap[$requestedProperty] ) )
00414                 $nameMap[$requestedProperty] = $requestedProperty;
00415         }
00416 
00417         // Misc helpful debug info (use when things go wrong..)
00418         //$this->appendLogEntry( var_dump( $requestedProperties ), 'outputCollectionContent' );
00419         //$this->appendLogEntry( var_dump( $nameMap ), 'outputCollectionContent' );
00420         //$this->appendLogEntry( var_dump( $collection ), 'outputCollectionContent' );
00421 
00422         // For all the entries in this dir/collection-array:
00423         foreach ( $collection as $entry )
00424         {
00425             // Translate the various UNIX timestamps to WebDAV format:
00426             $creationTime = date( eZWebDAVServer::CTIME_FORMAT, $entry['ctime'] );
00427             $modificationTime = date( eZWebDAVServer::MTIME_FORMAT, $entry['mtime'] );
00428 
00429             // The following lines take care of URL encoding special characters
00430             // for each element (stuff between slashes) in the path.
00431             $href = $entry['href'];
00432             $pathArray = split( '/', eZWebDAVServer::recode( "$href", $dataCharset, $xmlCharset ) );
00433 
00434             $encodedPath = '/';
00435 
00436             foreach( $pathArray as $pathElement )
00437             {
00438                 if( $pathElement != '' )
00439                 {
00440                     $encodedPath .= rawurlencode( $pathElement );
00441                     $encodedPath .= '/';
00442                 }
00443             }
00444 
00445             $isCollection = $entry['mimetype'] == 'httpd/unix-directory';
00446 
00447             // If this is not a collection, don't leave a trailing '/'
00448             // on the href. If you do, Goliath gets confused.
00449             if ( !$isCollection )
00450                 $encodedPath = rtrim($encodedPath, '/');
00451 
00452             $xmlText .= "<D:response>\n" .
00453                  " <D:href>" . $encodedPath ."</D:href>\n" .
00454                  " <D:propstat>\n" .
00455                  "  <D:prop>\n";
00456 
00457             $unknownProperties = array();
00458 
00459             foreach ( $requestedProperties as $requestedProperty )
00460             {
00461                 $name = $nameMap[$requestedProperty];
00462                 if ( isset( $entry[$name] ) )
00463                 {
00464                     if ( $requestedProperty == 'creationdate' )
00465                     {
00466                         $creationTime = date( eZWebDAVServer::CTIME_FORMAT, $entry['ctime'] );
00467                         $xmlText .= "   <D:" . $requestedProperty . ">" . $creationTime . "</D:" . $requestedProperty . ">\n";
00468                     }
00469                     else if ( $requestedProperty == 'getlastmodified' )
00470                     {
00471                         $modificationTime = date( eZWebDAVServer::MTIME_FORMAT, $entry['mtime'] );
00472                         $xmlText .= "   <D:" . $requestedProperty . ">" . $modificationTime . "</D:" . $requestedProperty . ">\n";
00473                     }
00474                     else if ( $isCollection and $requestedProperty == 'getcontenttype' )
00475                     {
00476 
00477                         $xmlText .= ( "   <D:resourcetype>\n" .
00478                                       "    <D:collection />\n" .
00479                                       "   </D:resourcetype>\n" );
00480 
00481                         $unknownProperties[] = $requestedProperty;
00482                     }
00483                     else
00484                     {
00485                         $xmlText .= "   <D:" . $requestedProperty . ">" . htmlspecialchars( eZWebDAVServer::recode( "$entry[$name]", $dataCharset, $xmlCharset ) ) . "</D:" . $requestedProperty . ">\n";
00486                     }
00487                 }
00488                 else if ( $requestedProperty != 'resourcetype' or !$isCollection )
00489                 {
00490                     $unknownProperties[] = $requestedProperty;
00491                 }
00492             }
00493 
00494             $xmlText .= ( "  <D:lockdiscovery/>\n" );
00495 
00496             $xmlText .= ( "  </D:prop>\n" .
00497                           "  <D:status>HTTP/1.1 200 OK</D:status>\n" .
00498                           " </D:propstat>\n" );
00499 
00500 
00501             // List the non supported properties and mark with 404
00502             // This behavior (although recommended/standard) might
00503             // confuse some clients. Try commenting out if necessary...
00504 
00505             $xmlText .= " <D:propstat>\n";
00506             $xmlText .= "  <D:prop>\n";
00507             foreach ( $unknownProperties as $unknownProperty )
00508             {
00509                 $xmlText .= "   <D:" . $unknownProperty . " />\n";
00510             }
00511             $xmlText .= "  </D:prop>\n";
00512             $xmlText .= "  <D:status>HTTP/1.1 404 Not Found</D:status>\n";
00513             $xmlText .= " </D:propstat>\n";
00514 
00515             $xmlText .= "</D:response>\n";
00516         }
00517 
00518         $xmlText .= "</D:multistatus>\n";
00519         // Send the necessary headers...
00520         header( 'HTTP/1.1 207 Multi-Status' );
00521         header( 'Content-Type: text/xml' );
00522 
00523         // Comment out the next line if you don't
00524         // want to use chunked transfer encoding.
00525         //header( 'Content-Length: '.strlen( $xmlText ) );
00526 
00527         $text = @ob_get_contents();
00528         if ( strlen( $text ) != 0 )
00529             $this->appendLogEntry( $text, "DAV: PHP Output" );
00530         while ( @ob_end_clean() );
00531 
00532         // Dump XML response (from server to client to logfile.
00533         //$this->appendLogEntry( $xmlText, 'xmlText' );
00534 
00535         // Dump the actual XML data containing collection list.
00536         print( $xmlText );
00537 
00538         $dom = new DOMDocument( '1.0', 'utf-8' );
00539         $success = $dom->loadXML( $xmlText );
00540         if ( $success )
00541             $this->appendLogEntry( "XML was parsed", 'outputCollectionContent' );
00542         else
00543             $this->appendLogEntry( "XML was NOT parsed $xmlText", 'outputCollectionContent' );
00544 
00545         // If we got this far: everything is OK.
00546         return eZWebDAVServer::OK_SILENT;
00547     }
00548 
00549     /*!
00550       \protected
00551       Outputs the data \a $output using print().
00552       \param $output Is an array which can contain:
00553                      - data - String or byte data
00554                      - file - The path to the file, the contents of the file will be output
00555       \return The WebDAV status code
00556     */
00557     function outputSendDataToClient( $output, $headers_only = false )
00558     {
00559         if ( !$output )
00560         {
00561             $this->appendLogEntry( "outputData: no data available", 'outputSendDataToClient' );
00562             return eZWebDAVServer::FAILED_NOT_FOUND;
00563         }
00564 
00565         // Check if we are dealing with custom data.
00566         if ( $output["data"] )
00567         {
00568             $this->appendLogEntry( "outputData: DATA is a string...", 'outputSendDataToClient' );
00569         }
00570         // Else: we need to output a file.
00571         elseif ( $output["file"] )
00572         {
00573             $this->appendLogEntry( "outputData: DATA is a file...", 'outputSendDataToClient' );
00574             $realPath = $output["file"];
00575 
00576             // Check if the file/dir actually exists and is readable (permission):
00577             if ( ( file_exists( $realPath ) ) && ( is_readable( $realPath ) ) )
00578             {
00579                 $this->appendLogEntry( "outputData: file exists on server...", 'outputSendDataToClient' );
00580 
00581                 // Get misc. file info.
00582                 $eTag = md5_file( $realPath );
00583                 $size = filesize( $realPath );
00584 
00585                 $dir  = dirname( $realPath );
00586                 $file = basename( $realPath );
00587 
00588                 $mimeInfo = eZMimeType::findByURL( $dir . '/' . $file );
00589                 $mimeType = $mimeInfo['name'];
00590 
00591                 // Send necessary headers to client.
00592                 header( 'HTTP/1.1 200 OK' );
00593                 header( 'Accept-Ranges: bytes' );
00594                 header( 'Content-Length: '.$size );
00595                 header( 'Content-Type: '.$mimeType );
00596                 header( 'ETag: '.$eTag );
00597 
00598                 $text = @ob_get_contents();
00599                 if ( strlen( $text ) != 0 )
00600                     $this->appendLogEntry( $text, "DAV: PHP Output" );
00601                 while ( @ob_end_clean() );
00602 
00603                 if ( !$headers_only )
00604                 {
00605                     // Attempt to open the file.
00606                     $fp = fopen( $realPath, "rb" );
00607 
00608                     // Output the actual contents of the file.
00609                     $status = fpassthru( $fp );
00610 
00611                     // Check if the last command succeded..
00612                     if ( $status == $size)
00613                     {
00614                         return eZWebDAVServer::OK_SILENT;
00615                     }
00616                     else
00617                     {
00618                         return eZWebDAVServer::FAILED_FORBIDDEN;
00619                     }
00620                 }
00621                 else
00622                 {
00623                     return eZWebDAVServer::OK_SILENT;
00624                 }
00625             }
00626             // Else: file/dir doesn't exist!
00627             else
00628             {
00629                 $this->appendLogEntry( "outputData: file DOES NOT exists on server...", 'outputSendDataToClient' );
00630                 return eZWebDAVServer::FAILED_NOT_FOUND;
00631             }
00632         }
00633         else
00634         {
00635             $this->appendLogEntry( "outputData: No file specified", 'outputSendDataToClient' );
00636 
00637             $text = @ob_get_contents();
00638             if ( strlen( $text ) != 0 )
00639                 $this->appendLogEntry( $text, "DAV: PHP Output" );
00640             while ( @ob_end_clean() );
00641 
00642             return eZWebDAVServer::FAILED_NOT_FOUND;
00643         }
00644     }
00645 
00646     /*!
00647       \protected
00648       Will try to store the uploaded to a temporary location using \a $target
00649       for name.
00650       \return The name of the temp file or \c false if it failed.
00651     */
00652     function storeUploadedFile( $target )
00653     {
00654         $dir = eZWebDAVServer::tempDirectory() . '/' . md5( microtime() . '-' . $target );
00655         $filePath = $dir . '/' . basename( $target );
00656 
00657         if ( !file_exists( $dir ) )
00658         {
00659             //include_once( 'lib/ezfile/classes/ezdir.php' );
00660             eZDir::mkdir( $dir, false, true );
00661         }
00662 
00663         $result = copy( "php://input", $filePath );
00664         if ( !$result )
00665         {
00666             $result = file_exists( $filePath );
00667         }
00668 
00669         if ( $result )
00670         {
00671             header( "HTTP/1.1 201 Created" );
00672             return $filePath;
00673         }
00674         return false;
00675     }
00676 
00677     /*!
00678       \return The XML body text for the current request.
00679     */
00680     function xmlBody()
00681     {
00682         $xmlBody = file_get_contents( "php://input" );
00683 //        $this->appendLogEntry( $xmlBody, 'xmlBody' );
00684         $this->XMLBodyRead = true;
00685         return $xmlBody;
00686     }
00687 
00688     /*!
00689       \return The XML body text for the current request.
00690     */
00691     function flushXMLBody()
00692     {
00693         // Flush the XML body by reading it,
00694         // PHP should discard it but it's a bug in some PHP versions
00695         $xmlBody = file_get_contents( "php://input" );
00696     }
00697 
00698     /*!
00699       \protected
00700       This method will be called on all intercepted URLs and can be reimplemented
00701       to clean up the URL for further processing.
00702       A typical usage is when the server is running without rewrite rules and will
00703       have the .php file in the path.
00704       \return The new URL which can safely be passed to the operation methods.
00705     */
00706     function processURL( $url )
00707     {
00708         return $url;
00709     }
00710 
00711     /*!
00712      \protected
00713      This is called before each each request is processed and can be used to output
00714      some common headers.
00715     */
00716     function headers()
00717     {
00718     }
00719 
00720     /*!
00721       \virtual
00722       Reports WebDAV options which information on what the server supports.
00723       \return An associative array with options, can contain:
00724               - methods - An array with methods it can handle,
00725                            if not supplied it will use all possible methods.
00726               - versions - An array with versions this server supports,
00727                             if not supplied it will use 1,2,<http://apache.org/dav/propset/fs/1>.
00728     */
00729     function options( $target )
00730     {
00731     }
00732 
00733     /*!
00734       \virtual
00735       \return An array with elements that belongs to the collection \a $collection
00736       \param $depth The current depth, \c 0 for only current object, \c 1 for it's children
00737       \param $properties Which properties the client asked for, either an array with DAV
00738                          property names, \c true for all properties or \c false for only property names.
00739     */
00740     function getCollectionContent( $collection, $depth = false, $properties = false )
00741     {
00742     }
00743 
00744     /*!
00745       \virtual
00746       \return Information on a given element
00747     */
00748     function head( $target )
00749     {
00750     }
00751 
00752     /*!
00753       \virtual
00754       Fetches the data for the element \a $target
00755       \return The contents of a given element, e.g. contents of a file.
00756     */
00757     function get( $target )
00758     {
00759     }
00760 
00761     /*!
00762       \virtual
00763       Tries to create/overwrite an element named \a $target with contents taken from \a $tempFile.
00764       \return The WebDAV status code
00765     */
00766     function put( $target, $tempFile )
00767     {
00768     }
00769 
00770     /*!
00771       \virtual
00772       Create a new collection (folder) named \a $target.
00773       \return The WebDAV status code
00774     */
00775     function mkcol( $target )
00776     {
00777     }
00778 
00779     /*!
00780       \virtual
00781       Copies the element \a $source to destination \a $destination
00782       \return The WebDAV status code
00783     */
00784     function copy( $source, $destination )
00785     {
00786     }
00787 
00788     /*!
00789       \virtual
00790       Moves the element \a $source to destination \a $destination
00791       \return The WebDAV status code
00792     */
00793     function move( $source, $destination )
00794     {
00795     }
00796 
00797     /*!
00798       \virtual
00799       Removes the element \a $target.
00800       \return The WebDAV status code
00801     */
00802     function delete( $target )
00803     {
00804     }
00805 
00806     /*!
00807       \protected
00808       Handles return values and sends necessary/corresponding headers.
00809     */
00810     function handle( $status )
00811     {
00812         $this->appendLogEntry( "handle function was called with status: $status", 'handle' );
00813 
00814         // Check and translate status to HTTP:WebDAV reply.
00815         switch ( $status )
00816         {
00817             // OK.
00818             case eZWebDAVServer::OK:
00819             {
00820                 header( "HTTP/1.1 200 OK" );
00821             } break;
00822 
00823             // OK, SILENT.
00824             case eZWebDAVServer::OK_SILENT:
00825             {
00826                 // Do nothing...
00827             } break;
00828 
00829             // OK, CREATED.
00830             case eZWebDAVServer::OK_CREATED:
00831             {
00832                 header( "HTTP/1.1 201 Created" );
00833             } break;
00834 
00835             // OK, OVERWRITE.
00836             case eZWebDAVServer::OK_OVERWRITE:
00837             {
00838                 header( "HTTP/1.1 204 No Content");
00839             } break;
00840 
00841             // FAILED, FORBIDDEN!
00842             case eZWebDAVServer::FAILED_FORBIDDEN:
00843             {
00844                 header( "HTTP/1.1 403 Forbidden");
00845             } break;
00846 
00847             // FAILED, NOT FOUND!
00848             case eZWebDAVServer::FAILED_NOT_FOUND:
00849             {
00850                 header( "HTTP/1.1 404 Not Found" );
00851             } break;
00852 
00853             // FAILED, ALREADY EXISTS!
00854             case eZWebDAVServer::FAILED_EXISTS:
00855             {
00856                 header( "HTTP/1.1 405 Method not allowed" );
00857             } break;
00858 
00859             // FAILED, CONFLICT!
00860             case eZWebDAVServer::FAILED_CONFLICT:
00861             {
00862                 header( "HTTP/1.1 409 Conflict" );
00863             }break;
00864 
00865             // FAILED, PRECONDITION.
00866             case eZWebDAVServer::FAILED_PRECONDITION:
00867             {
00868                 header( "HTTP/1.1 412 Precondition Failed" );
00869             } break;
00870 
00871             // FAILED, RESOURCE IS LOCKED!
00872             case eZWebDAVServer::FAILED_LOCKED:
00873             {
00874                 header( "HTTP/1.1 423 Locked" );
00875             } break;
00876 
00877             // FAILED, BAD GATEWAY!
00878             case eZWebDAVServer::FAILED_BAD_GATEWAY:
00879             {
00880                 header( "HTTP/1.1 502 Bad Gateway" );
00881             } break;
00882 
00883             // FAILED, NO SPACE LEFT ON DEVICE!
00884             case eZWebDAVServer::FAILED_STORAGE_FULL:
00885             {
00886                 header( "HTTP/1.1 507 Insufficient Storage" );
00887             } break;
00888 
00889             // FAILED, UNSUPPORTED REQUEST!
00890             case eZWebDAVServer::FAILED_UNSUPPORTED:
00891             {
00892                 header( "HTTP/1.1 415 Unsupported Media Type" );
00893             } break;
00894 
00895             // Default case: something went wrong...
00896             default:
00897             {
00898                 $this->appendLogEntry( "HTTP 500, THIS SHOULD NOT HAPPEN!", 'handle' );
00899                 header( "HTTP/1.1 500 Internal Server Error" );
00900             } break;
00901         }
00902 
00903         $text = @ob_get_contents();
00904         if ( strlen( $text ) != 0 )
00905             $this->appendLogEntry( $text, "DAV: PHP Output" );
00906         while ( @ob_end_clean() );
00907     }
00908 
00909     /*!
00910       Logs the string \a $logString to the logfile webdav.log
00911       in the current log directory (usually var/log).
00912       If logging is disabled, nothing is done.
00913     */
00914     static function appendLogEntry( $logString, $label = false )
00915     {
00916         if ( !eZWebDAVServer::isLoggingEnabled() )
00917             return false;
00918 
00919         $varDir = eZSys::varDirectory();
00920 
00921         $logDir = 'log';
00922         $logName = 'webdav.log';
00923         $fileName = $varDir . '/' . $logDir . '/' . $logName;
00924         if ( !file_exists( $varDir . '/' . $logDir ) )
00925         {
00926             //include_once( 'lib/ezfile/classes/ezdir.php' );
00927             eZDir::mkdir( $varDir . '/' . $logDir, false, true );
00928         }
00929 
00930         $logFile = fopen( $fileName, 'a' );
00931         $nowTime = date( "Y-m-d H:i:s : " );
00932         $text = $nowTime . $logString;
00933         if ( $label )
00934             $text .= ' [' . $label . ']';
00935         fwrite( $logFile, $text . "\n" );
00936         fclose( $logFile );
00937     }
00938 
00939     /*!
00940       \static
00941       \return \c true if WebDAV logging is enabled.
00942     */
00943     static function isLoggingEnabled()
00944     {
00945         $useLogging =& $GLOBALS['eZWebDavLogging'];
00946         if ( !isset( $useLogging ) )
00947         {
00948             $ini = eZINI::instance( 'webdav.ini' );
00949             $useLogging = $ini->variable( 'GeneralSettings', 'Logging' ) == 'enabled';
00950         }
00951         return $useLogging;
00952     }
00953 
00954     /*!
00955      Sets charset for outputted xml by 'userAgent'
00956     */
00957     function setupXMLOutputCharset()
00958     {
00959         $charset = eZWebDAVServer::dataCharset();
00960 
00961         $userAgent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : false;
00962         $pattern = eZWebDAVServer::userAgentPattern();
00963         $userAgentSettings = eZWebDAVServer::userAgentSettings();
00964 
00965         if ( preg_match( $pattern, $userAgent, $matches ) && isset( $userAgentSettings[$matches[0]] ) )
00966         {
00967             $agentSettings = $userAgentSettings[$matches[0]];
00968             if ( isset( $agentSettings['xmlCharset'] ) && $agentSettings['xmlCharset'] != '' )
00969                 $charset = $agentSettings['xmlCharset'];
00970         }
00971 
00972         $this->setXMLOutputCharset( $charset );
00973     }
00974 
00975     /*!
00976      Sets charset for outputted xml.
00977     */
00978     function setXMLOutputCharset( $charset )
00979     {
00980         if ( $charset == '' )
00981         {
00982             $this->appendLogEntry( "Error: unable to set empty charset for outputted xml.", 'setXMLOutputCharset' );
00983             return false;
00984         }
00985 
00986         $this->XMLOutputCharset = $charset;
00987         return true;
00988     }
00989 
00990     /*!
00991      \return charset for outputted xml
00992     */
00993     function XMLOutputCharset()
00994     {
00995         return $this->XMLOutputCharset;
00996     }
00997 
00998     /*!
00999      \static
01000      \return charset of data. It's used as charset for outputted xml if
01001      other charset is not specified in 'userAgentSettings'.
01002     */
01003     function dataCharset()
01004     {
01005         $ini = eZINI::instance( 'i18n.ini' );
01006         $charset = $ini->variable('CharacterSettings', 'Charset' );
01007         return $charset;
01008     }
01009 
01010     /*!
01011      \static
01012      \return pattern for 'preg_match'. The pattern is built from 'userAgentSettings'.
01013     */
01014     function userAgentPattern()
01015     {
01016         $pattern = '//';
01017 
01018         $userAgentSettings = eZWebDAVServer::userAgentSettings();
01019         if( count( $userAgentSettings ) > 0 )
01020         {
01021             $pattern = '/';
01022 
01023             foreach( $userAgentSettings as $agent => $settings )
01024                 $pattern .= $agent . '|';
01025 
01026             $pattern[strlen($pattern)-1] = '/';
01027         }
01028 
01029         return $pattern;
01030     }
01031 
01032     /*!
01033      \static
01034      \return a list of different settings for known user-agents.
01035     */
01036     function userAgentSettings()
01037     {
01038         return array( 'WebDrive' => array( 'xmlCharset' => 'utf-8' ),
01039                       'Microsoft Data Access Internet Publishing Provider' => array( 'xmlCharset' => 'utf-8' )
01040                     );
01041     }
01042 
01043     /*!
01044      \static
01045      \return recoded \a $string form \a $fromCharset to \a $toCharset
01046     */
01047     function recode( $string, $fromCharset, $toCharset, $stop = false )
01048     {
01049         //include_once( 'lib/ezi18n/classes/eztextcodec.php' );
01050         $codec = eZTextCodec::instance( $fromCharset, $toCharset, false );
01051         if ( $codec )
01052             $string = $codec->convertString( $string );
01053 
01054         return $string;
01055     }
01056 
01057     /*!
01058      \static
01059      \return the path to the WebDAV temporary directory
01060 
01061      If the directory does not exist yet, it will be created first.
01062     */
01063     static function tempDirectory()
01064     {
01065         $tempDir = eZSys::varDirectory() . '/webdav/tmp';
01066         if ( !file_exists( $tempDir ) )
01067         {
01068             eZDir::mkdir( $tempDir, false, true );
01069         }
01070         return $tempDir;
01071     }
01072 
01073     /*
01074      \static
01075      \return the path to the WebDAV root directory
01076 
01077      If the directory does not exist yet, it will be created first.
01078     */
01079     static function rootDirectory()
01080     {
01081         $rootDir = eZSys::varDirectory() . '/webdav/root';
01082         if ( !file_exists( $rootDir ) )
01083         {
01084             eZDir::mkdir( $rootDir, false, true );
01085         }
01086         return $rootDir;
01087     }
01088 
01089     /// \privatesection
01090     public $ServerRootDir = "";
01091     public $XMLBodyRead = false;
01092     public $XMLOutputCharset = 'utf-8';
01093 }
01094 ?>