diogenes.webdav.inc.php

00001 <?php
00002 /*
00003  * Copyright (C) 2003-2004 Polytechnique.org
00004  * http://opensource.polytechnique.org/
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 
00022 require_once 'HTTP/WebDAV/Server.php';
00023 require_once 'diogenes/diogenes.misc.inc.php';
00024 require_once 'diogenes.webdav.logger.inc.php';
00025 require_once 'Barrel.php';
00026 require_once 'Barrel/Page.php';
00027 
00032 class DiogenesWebDAV extends HTTP_WebDAV_Server
00033 {
00035   var $barrel;
00036 
00038   var $debug_handle;
00039 
00041   var $debug_capture;
00042   
00046   function DiogenesWebDAV()
00047   {
00048     global $globals;
00049 
00050     // call parent constructor
00051     $this->HTTP_WebDAV_Server();
00052 
00053     // construct new session each time, there is no practical way 
00054     // of tracking sessions with WebDAV clients
00055     $_SESSION['session'] = new $globals->session;
00056 
00057     $this->debug_capture = $globals->debugwebdav_capture;
00058     
00059     // init debug log if necessary
00060     if ($globals->debugwebdav) 
00061     {
00062       $this->debugInit($globals->debugwebdav);
00063       register_shutdown_function(array(&$this, 'debugClose'));
00064     }
00065       
00066   }
00067 
00068   
00075   function _urlencode($path, $for_html=false) {
00076     if ($this->barrel->vhost) {
00077       $path = preg_replace("/^(.*)\/site\/{$this->barrel->alias}\/(.*)/", "/\$2",$path);
00078     }
00079     return parent::_urlencode($path, $for_html);
00080   }
00081 
00082   
00090   function check_auth($type, $user, $pass)
00091   {
00092     global $globals;
00093 
00094     // WebDAV access is only granted for users who log in
00095     if (!$_SESSION['session']->doAuthWebDAV($user,$pass))
00096         return false;
00097 
00098     // we retrieve the user's permissions on the current barrel
00099     $_SESSION['session']->setBarrelPerms($this->barrel->alias);
00100 
00101     return true;
00102   }
00103 
00104 
00105   
00111   function copy($options)
00112   {
00113     // we do not allow copying files
00114     return "403 Forbidden";
00115   }
00116 
00117 
00118   
00119 
00123   function debugInit($debugfile)
00124   {
00125     if (empty($debugfile))
00126       return;
00127   
00128     // open the log file
00129     if ($fp = fopen($debugfile, "a")) 
00130     {
00131       $this->debug_handle = $fp;
00132     }
00133   }
00134 
00135 
00139   function debug($func, $msg)
00140   {
00141     if (isset($this->debug_handle))
00142     {
00143       $out = sprintf("[%s] %s : %s\n",date("H:i:s"), $func, $msg);
00144       fputs($this->debug_handle, $out);
00145     }
00146   }
00147  
00148    
00152   function debugClose()
00153   {
00154     if (isset($this->debug_handle))
00155     {
00156       fclose($this->debug_handle);
00157     }
00158   }
00159 
00160   
00166   function delete($options)
00167   {
00168     global $globals;
00169     $pathinfo = $this->parsePathInfo($options["path"]);
00170     
00171     // get the page ID and write permissions for the current path
00172     $pid = $this->barrel->getPID($pathinfo['dir']);
00173     if (!$pid || !$bpage = Diogenes_Barrel_Page::fromDb($this->barrel, $pid))
00174     {
00175       $this->debug('delete', "Could not find directory {$pathinfo['dir']}");
00176       return false;
00177     }
00178     
00179     // check permissions
00180     if (!$_SESSION['session']->hasPerms($bpage->props['wperms']))
00181     {
00182       $this->debug('delete', "Insufficient privileges (needed : {$bpage->props['wperms']})");
00183       return "403 Forbidden";
00184     }
00185 
00186     // create an RCS handle
00187     $rcs = $this->getRcs();
00188     $rcs->del($pid,$pathinfo['file']);
00189     return "204 No content";
00190   }
00191 
00192   
00199   function fileinfo($fspath, $uri)
00200   {
00201     global $globals;
00202 
00203     $file = array();
00204     $file["path"]= $uri;
00205 
00206     $file["props"][] = $this->mkprop("displayname", strtoupper($uri));
00207 
00208     $file["props"][] = $this->mkprop("creationdate", filectime($fspath));
00209     $file["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
00210 
00211     if (is_dir($fspath)) {
00212       $file["props"][] = $this->mkprop("getcontentlength", 0);
00213       $file["props"][] = $this->mkprop("resourcetype", "collection");
00214       $file["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
00215     } else {
00216       $file["props"][] = $this->mkprop("resourcetype", "");
00217       $file["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
00218       if (is_readable($fspath)) {
00219         $file["props"][] = $this->mkprop("getcontenttype", get_mime_type($fspath));
00220       } else {
00221         $file["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
00222       }
00223     }
00224     return $file;
00225   }
00226 
00227 
00233   function GET(&$options)
00234   {
00235     global $globals;
00236     $pathinfo = $this->parsePathInfo($options["path"]);
00237     
00238     // get the page ID and read permissions for the current path
00239     $pid = $this->barrel->getPID($pathinfo['dir']);
00240     if (!$pid || !$bpage = Diogenes_Barrel_Page::fromDb($this->barrel, $pid))
00241     {
00242       $this->debug('PROPFIND', "Could not find directory {$pathinfo['dir']}");
00243       return false;
00244     }
00245     
00246     // check permissions
00247     if (!$_SESSION['session']->hasPerms($bpage->props['perms']))
00248     {
00249       $this->debug('PROPFIND', "Insufficient privileges (needed : {$bpage->props['perms']})");
00250       return "403 Forbidden";
00251     }
00252 
00253     // create stream
00254     $fspath = $this->barrel->spool->spoolPath($pid,$pathinfo['file']);
00255     if (file_exists($fspath)) {             
00256       $options['mimetype'] = get_mime_type($fspath); 
00257                 
00258       // see rfc2518, section 13.7
00259       // some clients seem to treat this as a reverse rule
00260       // requiering a Last-Modified header if the getlastmodified header was set
00261       $options['mtime'] = filemtime($fspath);
00262       $options['size'] = filesize($fspath);
00263             
00264       // TODO check permissions/result
00265       $options['stream'] = fopen($fspath, "r");
00266 
00267       return true;
00268     } else {
00269       return false;
00270     }               
00271   }
00272 
00273     
00277   function getRcs()
00278   {
00279     global $globals;
00280     return new $globals->rcs($this,$this->barrel->alias,$_SESSION['session']->username);
00281   }
00282 
00283 
00289   function http_PROPFIND() 
00290   {
00291     $this->debug('http_PROPFIND', "called");
00292     return parent::http_PROPFIND();
00293   }
00294   
00295         
00302   function info($msg) {
00303     // we do nothing
00304   }
00305 
00306 
00314   function kill($msg) {
00315     $this->http_status("400 Error");
00316     exit;
00317   }
00318 
00319 
00328   function log($action,$data) {
00329     if (isset($_SESSION['log']) && is_object($_SESSION['log']))
00330       $_SESSION['log']->log($action,$data);
00331   }
00332 
00333   
00339   function MKCOL($options)
00340   {
00341     // we do not allow directory creations
00342     return "403 Forbidden";
00343   }
00344 
00345 
00351   function move($options)
00352   {
00353     // we do not allow moving files
00354     return "403 Forbidden";
00355   }
00356 
00357 
00364   function parsePathInfo($path) {
00365     global $globals;
00366 
00367     $this->debug('parsePathInfo', "path : $path");
00368     if (empty($path) || !preg_match("/^\/([^\/]+)\/webdav(\/((.+)\/)?([^\/]*))?$/",$path,$asplit))
00369       return false;
00370 
00371     $split['alias'] = $asplit[1];
00372     $split['dir'] = isset($asplit[4]) ? $asplit[4] : "";
00373     $split['file'] = isset($asplit[5]) ? $asplit[5] : "";
00374  
00375     // check that what we considered as a file is not in fact a directory
00376     // with a missing trailing slash
00377     /*
00378     if ( empty($split['dir']) and
00379         !empty($split['file']) and
00380         (mysql_num_rows($globals->db->query("select location from {$split['alias']}_page where location='{$split['file']}'"))>0))
00381     {
00382       $split['dir'] = $split['file'];
00383       $split['file'] = "";
00384     }
00385     */
00386     return $split;
00387   }
00388 
00395   function PROPFIND($options, &$files)
00396   {
00397     global $globals;
00398     $pathinfo = $this->parsePathInfo($options["path"]);
00399 
00400     // get the page ID and read permissions for the current path
00401     $pid = $this->barrel->getPID($pathinfo['dir']);
00402     if (!$pid || !$bpage = Diogenes_Barrel_Page::fromDb($this->barrel, $pid))
00403     {
00404       $this->debug('PROPFIND', "Could not find directory {$pathinfo['dir']}");
00405       return false;
00406     }
00407     
00408     // check permissions
00409     if (!$_SESSION['session']->hasPerms($bpage->props['perms']))
00410     {
00411       $this->debug('PROPFIND', "Insufficient privileges (needed : {$bpage->props['perms']})");
00412       return "403 Forbidden";
00413     }
00414     
00415     // get absolute fs path to requested resource
00416     $fspath = $this->barrel->spool->spoolPath($pid,$pathinfo['file']);
00417 
00418     // sanity check
00419     if (!file_exists($fspath)) {
00420       return false;
00421     }
00422 
00423     // prepare property array
00424     $files["files"] = array();
00425 
00426     // store information for the requested path itself
00427     $files["files"][] = $this->fileinfo($fspath, $options["path"]);
00428 
00429     // information for contained resources requested?
00430     if (!$pathinfo['file'] && !empty($options["depth"]))  { 
00431 
00432       // make sure path ends with '/'
00433       if (substr($options["path"],-1) != "/") {
00434         $options["path"] .= "/";
00435       }
00436 
00437       // list the sub-directories
00438       $res = $globals->db->query("select PID,location from {$this->barrel->table_page} where parent='$pid'");
00439       while (list($dpid,$dloc) = mysql_fetch_row($res)) {
00440         $dpath = $this->barrel->spool->spoolPath($dpid);
00441         $duri = $options["path"].$dloc;
00442         $files["files"][] = $this->fileinfo($dpath, $duri);
00443       }
00444       mysql_free_result($res);
00445       
00446       // now list the files in the current directory
00447       $handle = @opendir($fspath);
00448       if ($handle) {
00449         // ok, now get all its contents
00450         while ($filename = readdir($handle)) {
00451           if ($filename != "." && $filename != "..") {
00452             $fpath = $this->barrel->spool->spoolPath($pid,$filename);
00453             $furi = $options["path"].$filename;
00454             if (!is_dir($fpath))
00455               $files["files"][] = $this->fileinfo ($fpath, $furi);
00456           }
00457         }
00458       }
00459     }
00460 
00461     // ok, all done
00462     return true;
00463   }
00464 
00470   function proppatch(&$options)
00471   {
00472     global $prefs, $tab;
00473      
00474     $msg = "";
00475 
00476     $path = $options["path"];
00477 
00478     $dir = dirname($path)."/";
00479     $base = basename($path);
00480 
00481     foreach($options["props"] as $key => $prop) {
00482       if($ns == "DAV:") {
00483         $options["props"][$key][$status] = "403 Forbidden";
00484       }
00485     }
00486 
00487     return "";
00488   }
00489 
00495   function PUT(&$options)
00496   {
00497     global $globals;
00498     $pathinfo = $this->parsePathInfo($options["path"]);
00499 
00500     // we do not support multipart
00501     if (!empty($options["ranges"]))
00502       return "501";
00503     
00504     // get the page ID and write permissions for the current path
00505     $pid = $this->barrel->getPID($pathinfo['dir']);
00506     if (!$pid || !$bpage = Diogenes_Barrel_Page::fromDb($this->barrel, $pid))
00507     {
00508       $this->debug('PROPFIND', "Could not find directory {$pathinfo['dir']}");
00509       return false;
00510     }
00511     
00512     // check permissions
00513     if (!$_SESSION['session']->hasPerms($bpage->props['wperms']))
00514     {
00515       $this->debug('PROPFIND', "Insufficient privileges (needed : {$bpage->props['wperms']})");
00516       return "403 Forbidden";
00517     }
00518 
00519     // create an RCS handle
00520     $rcs = $this->getRcs();
00521     $options["new"] = !file_exists($rcs->rcsFile($pid,$pathinfo['file']));
00522 
00523     // read content
00524     $content = "";    
00525     while (!feof($options["stream"])) {
00526       $content .= fread($options["stream"], 4096);
00527     }
00528 
00529     // if this is a barrel page, strip extraneous tags
00530     if ($pathinfo['file'] == $globals->htmlfile)
00531       $content = $rcs->importHtmlString($content);
00532   
00533     // perform commit
00534     if (!$rcs->commit($pid,$pathinfo['file'],$content,"WebDAV PUT of {$pathinfo['file']}"))
00535       return "400 Error";
00536     
00537     // if this is Word master document, do the HTML conversion
00538     if ($globals->word_import && $pathinfo['file'] == $globals->wordfile) {
00539       $myfile = $this->barrel->spool->spoolPath($pid,$globals->wordfile);
00540       $rcs->importWordFile($pid, $globals->htmlfile, $myfile);
00541     }
00542     
00543     return $options["new"] ? "201 Created" : "204 No Content";
00544   }
00545 
00546   
00550   function ServeRequest()
00551   {
00552     global $globals;
00553  
00554     // break down path into site and location components
00555     if (!($pathinfo = $this->parsePathInfo($_SERVER['PATH_INFO'])))
00556     {
00557       $this->http_status("404 not found");
00558       exit;
00559     }
00560         
00561     // Retrieve site-wide info from database    
00562     $this->barrel = new Diogenes_Barrel($pathinfo['alias'], $this);  
00563     if (!$this->barrel->alias)
00564     {
00565       $this->debug("Could not find barrel '{$pathinfo['alias']}'");
00566       $this->http_status("404 not found");
00567       exit;      
00568     }
00569     $this->debug('ServeRequest', "barrel : ".$this->barrel->alias);    
00570     
00571     // Debugging info
00572     $props = array( 'REQUEST_METHOD', 'REQUEST_URI', 'SCRIPT_NAME', 'PATH_INFO');
00573     foreach ($props as $prop) {
00574       $this->debug('ServeRequest', "$prop : ". $_SERVER[$prop]);
00575     }    
00576 
00577     // Here we perform some magic on the script name (on PHP 4.3.10)
00578     // If the script is addressed by REQUEST_URI /site/foo/bar/ :
00579     //  - Apache 1.x returns /site as the SCRIPT_NAME
00580     //  - Apache 2.x returns /site.php as the SCRIPT_NAME
00581     //
00582     // We set SCRIPT_NAME to match the REQUEST_URI minus the PATH_INFO
00583     //
00584     $_SERVER['SCRIPT_NAME'] = substr($_SERVER['REQUEST_URI'], 0, - strlen($_SERVER['PATH_INFO']));        
00585     $this->debug('ServeRequest', "SCRIPT_NAME(mod) : ". $_SERVER['SCRIPT_NAME']);
00586     
00587     // turn on output buffering
00588     ob_start();      
00589     
00590     // let the base class do all the work   
00591     parent::ServeRequest();
00592     
00593     // stop output buffering
00594     $out = ob_get_contents();
00595     ob_end_clean();
00596 
00597     // to support barrels on virtualhosts, we rewrite the URLs    
00598     if ($this->barrel->vhost)
00599     {    
00600       $ohref = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:");
00601       $ohref.= "//".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'];
00602       $ohref.= "/" . $this->barrel->alias . "/webdav/";
00603       $this->debug('ServeRequest', "ohref : ".$ohref);      
00604         
00605       $vhref = (@$_SERVER['HTTPS'] === "on" ? "https:" : "http:");
00606       $vhref.= "//".$this->barrel->vhost . "/webdav/";
00607       $this->debug('ServeRequest', "vhref : ".$vhref);  
00608       
00609       $out = str_replace($ohref, $vhref, $out);
00610       $out = str_replace('<D:displayname>/'.strtoupper($this->barrel->alias).'/WEBDAV/', '<D:displayname>/WEBDAV/', $out);
00611     }
00612     
00613     // send output
00614     echo $out;
00615         
00616     // if requested, log the output that is sent to the client
00617     if ($this->debug_capture)
00618     {
00619       $this->debug('ServeRequest', "out\n--- begin --\n$out--- end ---\n");    
00620     }    
00621     
00622   }
00623 
00624 
00625 }
00626 
00627 ?>

Generated on Fri Jan 11 01:20:08 2008 for Diogenes by  doxygen 1.5.1