ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
class.ilDAVServer.php
Go to the documentation of this file.
1 <?php
2 // BEGIN WebDAV
3 /*
4  +-----------------------------------------------------------------------------+
5  | ILIAS open source |
6  +-----------------------------------------------------------------------------+
7  | Copyright (c) 1998-2005 ILIAS open source, University of Cologne |
8  | |
9  | This program is free software; you can redistribute it and/or |
10  | modify it under the terms of the GNU General Public License |
11  | as published by the Free Software Foundation; either version 2 |
12  | of the License, or (at your option) any later version. |
13  | |
14  | This program is distributed in the hope that it will be useful, |
15  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17  | GNU General Public License for more details. |
18  | |
19  | You should have received a copy of the GNU General Public License |
20  | along with this program; if not, write to the Free Software |
21  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22  +-----------------------------------------------------------------------------+
23 */
24 
25 require_once 'Services/WebDAV/classes/Server.php';
26 require_once 'Services/WebDAV/classes/class.ilDAVLocks.php';
27 require_once 'Services/WebDAV/classes/class.ilDAVProperties.php';
28 require_once 'Services/WebDAV/classes/class.ilObjectDAV.php';
29 
30 require_once 'Services/User/classes/class.ilObjUser.php';
31 require_once 'Services/Utilities/classes/class.ilFileUtils.php';
32 require_once 'include/Unicode/UtfNormal.php';
33 require_once 'Services/Tracking/classes/class.ilChangeEvent.php';
34 
52 {
57  private static $instance = null;
58 
64 
68  private $locks;
72  private $properties;
73 
79  private $clientOS = 'unknown';
84  private $clientOSFlavor = 'unknown';
89  private $clientBrowser = 'unknown';
90 
101  private $putObjDAV = null;
102 
109  private $isHTTPS = null;
110 
115  private $isDebug = false;
116 
126  public function ilDAVServer()
127  {
128  $this->writelog("<constructor>");
129 
130  // Initialize the WebDAV server and create
131  // locking and property support objects
132  $this->HTTP_WebDAV_Server();
133  $this->locks = new ilDAVLocks();
134  $this->properties = new ilDAVProperties();
135  //$this->locks->createTable();
136  //$this->properties->createTable();
137 
138  // Guess operating system, operating system flavor and browser of the webdav client
139  //
140  // - We need to know the operating system in order to properly
141  // hide hidden resources in directory listings.
142  //
143  // - We need the operating system flavor and the browser to
144  // properly support mounting of a webdav folder.
145  //
146  $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
147  $this->writelog('userAgent='.$userAgent);
148  if (strpos($userAgent,'windows') !== false
149  || strpos($userAgent,'microsoft') !== false)
150  {
151  $this->clientOS = 'windows';
152  if(strpos($userAgent,'nt 5.1') !== false){
153  $this->clientOSFlavor = 'xp';
154  }else{
155  $this->clientOSFlavor = 'nichtxp';
156  }
157 
158  } else if (strpos($userAgent,'darwin') !== false
159  || strpos($userAgent,'macintosh') !== false
160  || strpos($userAgent,'linux') !== false
161  || strpos($userAgent,'solaris') !== false
162  || strpos($userAgent,'aix') !== false
163  || strpos($userAgent,'unix') !== false
164  || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
165  )
166  {
167  $this->clientOS = 'unix';
168  if (strpos($userAgent,'linux') !== false)
169  {
170  $this->clientOSFlavor = 'linux';
171  }
172  else if (strpos($userAgent,'macintosh') !== false)
173  {
174  $this->clientOSFlavor = 'osx';
175  }
176  }
177  if (strpos($userAgent,'konqueror') !== false)
178  {
179  $this->clientBrowser = 'konqueror';
180  }
181  }
182 
187  public static function getInstance()
188  {
189  if(self::$instance != NULL)
190  {
191  return self::$instance;
192  }
193  return self::$instance = new ilDAVServer();
194  }
195 
199  public function serveRequest()
200  {
201  // die quickly if plugin is deactivated
202  if (!self::_isActive())
203  {
204  $this->writelog(__METHOD__.' WebDAV disabled. Aborting');
205  $this->http_status('403 Forbidden');
206  echo '<html><body><h1>Sorry</h1>'.
207  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
208  '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
209  '</body></html>';
210  return;
211  }
212 
213  try {
214  $start = time();
215  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
216  parent::serveRequest();
217  $end = time();
218  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
219  $this->writelog('---');
220  }
221  catch (Exception $e)
222  {
223  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
224  }
225  }
226 
261  private function davUrlEncode($path)
262  {
263  // We compose the path to Unicode Normal Form NFC
264  // This ensures that diaeresis and other special characters
265  // are treated uniformly on Windows and on Mac OS X
267 
268  $c = explode('/',$path);
269  for ($i = 0; $i < count($c); $i++)
270  {
271  $c[$i] = str_replace('+','%20',urlencode($c[$i]));
272  }
273  return implode('/',$c);
274  }
275 
283  public function PROPFIND(&$options, &$files)
284  {
285  // Activate tree cache
286  global $tree;
287  //$tree->useCache(true);
288 
289  $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
290  $this->writelog('PROPFIND '.$options['path']);
291 
292  // get dav object for path
293  $path =& $this->davDeslashify($options['path']);
294  $objDAV =& $this->getObject($path);
295 
296  // prepare property array
297  $files['files'] = array();
298 
299  // sanity check
300  if (is_null($objDAV)) {
301  return false;
302  }
303  if (! $objDAV->isPermitted('visible,read')) {
304  return '403 Forbidden';
305  }
306 
307  // store information for the requested path itself
308  // FIXME : create display name for object.
309  $encodedPath = $this->davUrlEncode($path);
310 
311  $GLOBALS['ilLog']->write(print_r($encodedPath,true));
312 
313  $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
314 
315  // information for contained resources requested?
316  if (!empty($options['depth'])) {
317  // The breadthFirst list holds the collections which we have not
318  // processed yet. If depth is infinity we append unprocessed collections
319  // to the end of this list, and remove processed collections from
320  // the beginning of this list.
321  $breadthFirst = array($objDAV);
322  $objDAV->encodedPath = $encodedPath;
323 
324  while (count($breadthFirst) > 0) {
325  // remove a collection from the beginning of the breadthFirst list
326  $collectionDAV = array_shift($breadthFirst);
327  $childrenDAV =& $collectionDAV->childrenWithPermission('visible,read');
328  foreach ($childrenDAV as $childDAV)
329  {
330  // On duplicate names, work with the older object (the one with the
331  // smaller object id).
332  foreach ($childrenDAV as $duplChildDAV)
333  {
334  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
335  $duplChildDAV->getResourceName() == $childDAV->getResourceName())
336  {
337  continue 2;
338  }
339  }
340 
341  // only add visible objects to the file list
342  if (!$this->isFileHidden($childDAV))
343  {
344  $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
345  $files['files'][] =& $this->fileinfo(
346  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
347  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
348  $childDAV
349  );
350  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
351  // add a collection to the end of the breadthFirst list
352  $breadthFirst[] = $childDAV;
353  $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
354  }
355  }
356  }
357  }
358  }
359 
360  // Record read event but don't catch up with write events, because
361  // with WebDAV, a user can not see all objects contained in a folder.
362  global $ilUser;
363  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
364  $objDAV->getObjectId(), $ilUser->getId(), false);
365 
366  // ok, all done
367  $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
368  return true;
369  }
370 
381  private function isFileHidden(&$objDAV)
382  {
383  // Hide null resources which haven't got an active lock
384  if ($objDAV->isNullResource()) {
385  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
386  return;
387  }
388  }
389 
390  $name = $objDAV->getResourceName();
391  $isFileHidden = false;
392  switch ($this->clientOS)
393  {
394  case 'unix' :
395  // Hide Windows thumbnail files, and files which start with '~$'.
396  $isFileHidden =
397  $name == 'Thumbs.db'
398  || substr($name, 0, 2) == '~$';
399  // Hide files which contain /
400  $isFileHidden |= preg_match('/\\//', $name);
401  break;
402  case 'windows' :
403  // Hide files that start with '.'.
404  $isFileHidden = substr($name, 0, 1) == '.';
405  // Hide files which contain \ / : * ? " < > |
406  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
407  break;
408  default :
409  // Hide files which contain /
410  $isFileHidden |= preg_match('/\\//', $name);
411  break;
412  }
413  $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
414  return $isFileHidden;
415  }
416 
424  private function fileinfo($resourcePath, $displayPath, &$objDAV)
425  {
426  global $ilias;
427 
428  $this->writelog('fileinfo('.$resourcePath.')');
429  // create result array
430  $info = array();
431  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
432  * end of a resource path to a collection.
433  * However Mac OS X does not like this!
434  */
435  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
436  $info['path'] = $resourcePath.'/';
437  } else {
438  $info['path'] = $resourcePath;
439  }
440 
441  $info['props'] = array();
442 
443  // no special beautified displayname here ...
444  $info["props"][] =& $this->mkprop("displayname", $displayPath);
445 
446  // creation and modification time
447  $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
448  $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
449 
450  // directory (WebDAV collection)
451  $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
452  $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
453  $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
454 
455  // Only show supported locks for users who have write permission
456  if ($objDAV->isPermitted('write'))
457  {
458  $info["props"][] =& $this->mkprop("supportedlock",
459  '<D:lockentry>'
460  .'<D:lockscope><D:exclusive/></D:lockscope>'
461  .'<D:locktype><D:write/></D:locktype>'
462  .'</D:lockentry>'
463  .'<D:lockentry>'
464  .'<D:lockscope><D:shared/></D:lockscope>'
465  .'<D:locktype><D:write/></D:locktype>'
466  .'</D:lockentry>'
467  );
468  }
469 
470  // Maybe we should only show locks on objects for users who have write permission.
471  // But if we don't show these locks, users who have write permission in an object
472  // further down in a hierarchy can't see who is locking their object.
473  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
474  $lockdiscovery = '';
475  foreach ($locks as $lock)
476  {
477  // DAV Clients expects to see their own owner name in
478  // the locks. Since these names are not unique (they may
479  // just be the name of the local user running the DAV client)
480  // we return the ILIAS user name in all other cases.
481  if ($lock['ilias_owner'] == $ilias->account->getId())
482  {
483  $owner = $lock['dav_owner'];
484  } else {
485  $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
486  }
487  $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
488 
489  $lockdiscovery .=
490  '<D:activelock>'
491  .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
492  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
493  .'<D:locktype><D:write/></D:locktype>'
494  .'<D:depth>'.$lock['depth'].'</D:depth>'
495  .'<D:owner>'.$owner.'</D:owner>'
496 
497  // more than a million is considered an absolute timestamp
498  // less is more likely a relative value
499  .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
500  .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
501  .'</D:activelock>'
502  ;
503  }
504  if (strlen($lockdiscovery) > 0)
505  {
506  $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
507  }
508 
509  // get additional properties from database
510  $properties = $this->properties->getAll($objDAV);
511  foreach ($properties as $prop)
512  {
513  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
514  }
515 
516  //$this->writelog('fileinfo():'.var_export($info, true));
517  return $info;
518  }
519 
531  public function GET(&$options)
532  {
533  global $ilUser;
534 
535  $this->writelog('GET('.var_export($options, true).')');
536  $this->writelog('GET('.$options['path'].')');
537 
538  // get dav object for path
539  $path = $this->davDeslashify($options['path']);
540  $objDAV =& $this->getObject($path);
541 
542  // sanity check
543  if (is_null($objDAV) || $objDAV->isNullResource())
544  {
545  return false;
546  }
547 
548  if (! $objDAV->isPermitted('visible,read'))
549  {
550  return '403 Forbidden';
551  }
552 
553  // is this a collection?
554  if ($objDAV->isCollection())
555  {
556  if (isset($_GET['mount']))
557  {
558  return $this->mountDir($objDAV, $options);
559  }
560  else if (isset($_GET['mount-instructions']))
561  {
562  return $this->showMountInstructions($objDAV, $options);
563  }
564  else
565  {
566  return $this->getDir($objDAV, $options);
567  }
568  }
569  // detect content type
570  $options['mimetype'] =& $objDAV->getContentType();
571  // detect modification time
572  // see rfc2518, section 13.7
573  // some clients seem to treat this as a reverse rule
574  // requiring a Last-Modified header if the getlastmodified header was set
575  $options['mtime'] =& $objDAV->getModificationTimestamp();
576 
577  // detect content length
578  $options['size'] =& $objDAV->getContentLength();
579 
580  // get content as stream or as data array
581  $options['stream'] =& $objDAV->getContentStream();
582  if (is_null($options['stream']))
583  {
584  $options['data'] =& $objDAV->getContentData();
585  }
586 
587  // Record read event and catch up write events
588  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
589  $objDAV->getObjectId(), $ilUser->getId());
590 
591  $this->writelog('GET:'.var_export($options, true));
592 
593  return true;
594  }
606  private function mountDir(&$objDAV, &$options)
607  {
608  $path = $this->davDeslashify($options['path']);
609 
610  header('Content-Type: application/davmount+xml');
611 
612  echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
613  echo " </dm:url>".$this->base_uri."</dm:url>\n";
614 
615  $xmlPath = str_replace('&','&amp;',$path);
616  $xmlPath = str_replace('<','&lt;',$xmlPath);
617  $xmlPath = str_replace('>','&gt;',$xmlPath);
618 
619  echo " </dm:open>$xmlPath</dm:open>\n";
620  echo "</dm:mount>\n";
621 
622  exit;
623 
624  }
631  private function showMountInstructions(&$objDAV, &$options)
632  {
633  global $lng,$ilUser;
634 
635  $path = $this->davDeslashify($options['path']);
636 
637  // The $path variable may contain a full or a shortened DAV path.
638  // We convert it into an object path, which we can then use to
639  // construct a new full DAV path.
640  $objectPath = $this->toObjectPath($path);
641 
642  // Construct a (possibly) full DAV path from the object path.
643  $fullPath = '';
644  foreach ($objectPath as $object)
645  {
646  if ($object->getRefId() == 1 && $this->isFileHidden($object))
647  {
648  // If the repository root object is hidden, we can not
649  // create a full path, because nothing would appear in the
650  // webfolder. We resort to a shortened path instead.
651  $fullPath .= '/ref_1';
652  }
653  else
654  {
655  $fullPath .= '/'.$this->davUrlEncode($object->getResourceName());
656  }
657  }
658 
659  // Construct a shortened DAV path from the object path.
660  $shortenedPath = '/ref_'.
661  $objectPath[count($objectPath) - 1]->getRefId();
662 
663  if ($objDAV->isCollection())
664  {
665  $shortenedPath .= '/';
666  $fullPath .= '/';
667  }
668 
669  // Prepend client id to path
670  $shortenedPath = '/'.CLIENT_ID.$shortenedPath;
671  $fullPath = '/'.CLIENT_ID.$fullPath;
672 
673  // Construct webfolder URI's. The URI's are used for mounting the
674  // webfolder. Since mounting using URI's is not standardized, we have
675  // to create different URI's for different browsers.
676  $webfolderURI = $this->base_uri.$shortenedPath;
677  $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav").
678  substr($this->base_uri, strrpos($this->base_uri,':')).
679  $shortenedPath;
680  ;
681  $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav").
682  substr($this->base_uri, strrpos($this->base_uri,':')).
683  $shortenedPath
684  ;
685  $webfolderURI_IE = $this->base_uri.$shortenedPath;
686 
687  $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
688 
689  header('Content-Type: text/html; charset=UTF-8');
690  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
691  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
692  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
693  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
694  echo " <head>\n";
695  echo " <title>".sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle)."</title>\n";
696  echo " </head>\n";
697  echo " <body>\n";
698 
699  echo ilDAVServer::_getWebfolderInstructionsFor($webfolderTitle,
700  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
701  $this->clientOS,$this->clientOSFlavor);
702 
703  echo " </body>\n";
704  echo "</html>\n";
705 
706  // Logout anonymous user to force authentication after calling mount uri
707  if($ilUser->getId() == ANONYMOUS_USER_ID)
708  {
709  $GLOBALS['ilAuth']->logout();
710  session_destroy();
711  }
712 
713  exit;
714  }
724  private function getDir(&$objDAV, &$options)
725  {
726  global $ilias, $lng;
727 
728  // Activate tree cache
729  global $tree;
730  //$tree->useCache(true);
731 
732  $path = $this->davDeslashify($options['path']);
733 
734  // The URL of a directory must end with a slash.
735  // If it does not we are redirecting the browser.
736  // The slash is required, because we are using relative links in the
737  // HTML code we are generating below.
738  if ($path.'/' != $options['path'])
739  {
740  header('Location: '.$this->base_uri.$path.'/');
741  exit;
742  }
743 
744  header('Content-Type: text/html; charset=UTF-8');
745 
746  // fixed width directory column format
747  $format = "%15s %-19s %-s\n";
748 
749  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
750  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
751  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
752  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
753  echo "<head>\n";
754  echo "<title>".sprintf($lng->txt('webfolder_index_of'), $path)."</title>\n";
755 
756  // Create "anchorClick" behavior for for Internet Explorer
757  // This allows to create a link to a webfolder
758  echo "<style type=\"text/css\">\n";
759  echo "<!--\n";
760  echo "a {\n";
761  echo " behavior:url(#default#AnchorClick);\n";
762  echo "}\n";
763  echo "-->\n";
764  echo "</style>\n";
765 
766  echo "</head><body>\n";
767 
768  $hrefPath = '';
769  $pathComponents = explode('/',$path);
770  $uriComponents = array();
771  foreach ($pathComponents as $component)
772  {
773  $uriComponents[] = $this->davUrlEncode($component);
774  }
775  for ($i = 0; $i < count($pathComponents); $i++)
776  {
777  $displayName = htmlspecialchars($pathComponents[$i]);
778  if ($i != 0) {
779  $hrefPath .= '/';
780  }
781  $uriPath = implode('/', array_slice($uriComponents,0,$i + 1));
782  if ($i < 2)
783  {
784  // The first two path elements consist of the webdav.php script
785  // and the client id. These elements are not part of the
786  // directory structure and thus are not represented as links.
787  $hrefPath .= $displayName;
788  }
789  else
790  {
791  $hrefPath .= '<a href="'.$this->base_uri.$uriPath.'/">'.$displayName.'</a>';
792  }
793  }
794  echo "<h3>".sprintf($lng->txt('webfolder_index_of'), $hrefPath)."</h3>\n";
795 
796  // Display user id
797  if ($ilias->account->getLogin() == 'anonymous')
798  {
799  echo "<p><font size=\"-1\">".$lng->txt('not_logged_in')."</font><br>\n";
800  } else {
801  echo "<p><font size=\"-1\">".$lng->txt('login_as')." <i>"
802  .$ilias->account->getFirstname().' '
803  .$ilias->account->getLastname().' '
804  .' '.$ilias->account->getLogin().'</i> '
805  .', '.$lng->txt('client').' <i>'.$ilias->getClientId().'</i>.'
806  ."</font><p>\n";
807  }
808 
809  // Create "open as webfolder" link
810  $href = $this->base_uri.$uriPath;
811  // IE can not mount long paths. If the path has more than one element, we
812  // create a relative path with a ref-id.
813  if (count($pathComponents) > 2)
814  {
815  $hrefIE = $this->base_uri.'/'.CLIENT_ID.'/ref_'.$objDAV->getRefId();
816  } else {
817  $hrefIE = $href;
818  }
819  echo "<p><font size=\"-1\">".
820  sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions").
821  "</font></p>\n";
822  echo "<p><font size=\"-1\">".
823  sprintf($lng->txt('webfolder_mount_dir_with'),
824  "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
825  'webdav'.substr($href,4), // Konqueror
826  'dav'.substr($href,4), // Nautilus
827  $href.'?mount' // RFC 4709
828  )
829  ."</font></p>\n";
830 
831  echo "<pre>";
832  printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
833  echo "<hr>";
834 
835  $collectionCount = 0;
836  $fileCount = 0;
837  $children =& $objDAV->childrenWithPermission('visible,read');
838  foreach ($children as $childDAV) {
839  if ($childDAV->isCollection() && !$this->isFileHidden($childDAV))
840  {
841  $collectionCount++;
842  $name = $this->davUrlEncode($childDAV->getResourceName());
843  printf($format,
844  '-',
845  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
846  '<a href="'.$name.'/'.'">'.$childDAV->getDisplayName()."</a>");
847  }
848  }
849  foreach ($children as $childDAV) {
850  if ($childDAV->isFile() && !$this->isFileHidden($childDAV))
851  {
852  $fileCount++;
853  $name = $this->davUrlEncode($childDAV->getResourceName());
854  printf($format,
855  number_format($childDAV->getContentLength()),
856  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
857  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
858  }
859  }
860  foreach ($children as $childDAV) {
861  if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV))
862  {
863  $name = $this->davUrlEncode($childDAV->getResourceName());
864  printf($format,
865  'Lock',
866  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
867  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
868  }
869  }
870  echo "<hr>";
871  echo $collectionCount.' '.$lng->txt(($collectionCount == 1) ? 'folder' : 'folders').', ';
872  echo $fileCount.' '.$lng->txt(($fileCount == 1) ? 'file' : 'files').'.';
873  echo "</pre>";
874  echo "</body></html>\n";
875 
876  exit;
877  }
878 
879 
886  public function PUT(&$options)
887  {
888  global $ilUser;
889 
890  $this->writelog('PUT('.var_export($options, true).')');
891 
892  $path = $this->davDeslashify($options['path']);
893  $parent = dirname($path);
894  $name = $this->davBasename($path);
895 
896  //Check if FileType is allowed
897  if ($name != ilFileUtils::getValidFilename($name)) {
898  return false;
899  }
900 
901  // get dav object for path
902  $parentDAV =& $this->getObject($parent);
903 
904  // sanity check
905  if (is_null($parentDAV) || ! $parentDAV->isCollection()) {
906  return '409 Conflict';
907  }
908 
909  // Prevent putting of files which exceed upload limit
910  // FIXME: since this is an optional parameter, we should to do the
911  // same check again in function PUTfinished.
912  if ($options['content_length'] != null &&
913  $options['content_length'] > $this->getUploadMaxFilesize()) {
914 
915  $this->writelog('PUT is forbidden, because content length='.
916  $options['content_length'].' is larger than upload_max_filesize='.
917  $this->getUploadMaxFilesize().'in php.ini');
918 
919  return '403 Forbidden';
920  }
921 
922  // determine mime type
923  include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
924  $mime = ilMimeTypeUtil::lookupMimeType($name);
925 
926  $objDAV =& $this->getObject($path);
927  if (is_null($objDAV))
928  {
929  $ttype = $parentDAV->getILIASFileType();
930  $isperm = $parentDAV->isPermitted('create', $ttype);
931  if (! $isperm)
932  {
933  $this->writelog('PUT is forbidden, because user has no create permission');
934 
935  return '403 Forbidden';
936  }
937  $options["new"] = true;
938  $objDAV =& $parentDAV->createFile($name);
939  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
940  //$objDAV->setContentType($options['content_type']);
941  $objDAV->setContentType($mime);
942  if ($options['content_length'] != null)
943  {
944  $objDAV->setContentLength($options['content_length']);
945  }
946  $objDAV->write();
947  // Record write event
948  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
949  }
950  else if ($objDAV->isNullResource())
951  {
952  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
953  {
954  $this->writelog('PUT is forbidden, because user has no create permission');
955  return '403 Forbidden';
956  }
957  $options["new"] = false;
958  $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
959  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
960  //$objDAV->setContentType($options['content_type']);
961  $objDAV->setContentType($mime);
962  if ($options['content_length'] != null)
963  {
964  $objDAV->setContentLength($options['content_length']);
965  }
966  $objDAV->write();
967 
968  // Record write event
969  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
970  }
971  else
972  {
973  if (! $objDAV->isPermitted('write'))
974  {
975  $this->writelog('PUT is forbidden, because user has no write permission');
976  return '403 Forbidden';
977  }
978  $options["new"] = false;
979  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type'].' content_length='.$options['content_length']);
980 
981  // Create a new version if the previous version is not empty
982  if ($objDAV->getContentLength() != 0) {
983  $objDAV->createNewVersion();
984  }
985 
986  //$objDAV->setContentType($options['content_type']);
987  $objDAV->setContentType($mime);
988  if ($options['content_length'] != null)
989  {
990  $objDAV->setContentLength($options['content_length']);
991  }
992  $objDAV->write();
993 
994  // Record write event
995  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
996  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
997  }
998  // store this object, we reuse it in method PUTfinished
999  $this->putObjDAV = $objDAV;
1000 
1001  $out =& $objDAV->getContentOutputStream();
1002  $this->writelog('PUT outputstream='.$out);
1003 
1004  return $out;
1005  }
1006 
1013  public function PUTfinished(&$options)
1014  {
1015  $this->writelog('PUTfinished('.var_export($options, true).')');
1016 
1017  if($this->putObjDAV->getResourceType()==""){
1018  $vir = ilUtil::virusHandling($this->putObjDAV->obj->getDirectory($this->putObjDAV->obj->version).'/'.$this->putObjDAV->obj->filename, $this->putObjDAV->obj->filename);
1019  if ($vir[0] == false)
1020  {
1021  $this->writelog('PUTfinished Virus found: '.$vir[1]);
1022  //delete file
1024  return false;
1025  }
1026  }
1027 
1028  // Update the content length in the file object, if the
1029  // the client did not specify a content_length
1030  if ($options['content_length'] == null || $this->putObjDAV->getContentLength() == 0)
1031  {
1032  $objDAV = $this->putObjDAV;
1033  if ($objDAV->getContentOutputStreamLength() != null) {
1034  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1035  } else {
1036  $objDAV->write();
1037  $objDAV->setContentLength(filesize($objDAV->obj->getDirectory($objDAV->obj->version).'/'.$objDAV->obj->filename));
1038  }
1039  $objDAV->write();
1040  $this->putObjDAV = null;
1041  }
1042  return true;
1043  }
1044 
1045 
1052  public function MKCOL($options)
1053  {
1054  global $ilUser;
1055 
1056  $this->writelog('MKCOL('.var_export($options, true).')');
1057  $this->writelog('MKCOL '.$options['path']);
1058 
1059  $path =& $this->davDeslashify($options['path']);
1060  $parent =& dirname($path);
1061  $name =& $this->davBasename($path);
1062 
1063  // No body parsing yet
1064  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1065  return "415 Unsupported media type";
1066  }
1067 
1068  // Check if an object with the path already exists.
1069  $objDAV =& $this->getObject($path);
1070  if (! is_null($objDAV))
1071  {
1072  return '405 Method not allowed';
1073  }
1074 
1075  // get parent dav object for path
1076  $parentDAV =& $this->getObject($parent);
1077 
1078  // sanity check
1079  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1080  {
1081  return '409 Conflict';
1082  }
1083 
1084  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1085  {
1086  return '403 Forbidden';
1087  }
1088 
1089  // XXX Implement code that Handles null resource here
1090 
1091  $objDAV = $parentDAV->createCollection($name);
1092 
1093  if ($objDAV != null)
1094  {
1095  // Record write event
1096  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1097  }
1098 
1099  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1100  return $result;
1101  }
1102 
1103 
1110  public function DELETE($options)
1111  {
1112  global $ilUser;
1113 
1114  $this->writelog('DELETE('.var_export($options, true).')');
1115  $this->writelog('DELETE '.$options['path']);
1116 
1117  // get dav object for path
1118  $path =& $this->davDeslashify($options['path']);
1119  $parentDAV =& $this->getObject(dirname($path));
1120  $objDAV =& $this->getObject($path);
1121 
1122  // sanity check
1123  if (is_null($objDAV) || $objDAV->isNullResource())
1124  {
1125  return '404 Not Found';
1126  }
1127  if (! $objDAV->isPermitted('delete'))
1128  {
1129  return '403 Forbidden';
1130  }
1131 
1132  $parentDAV->remove($objDAV);
1133 
1134  // Record write event
1135  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1136 
1137  return '204 No Content';
1138  }
1139 
1146  public function MOVE($options)
1147  {
1148  global $ilUser;
1149 
1150  $this->writelog('MOVE('.var_export($options, true).')');
1151  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1152 
1153  // Get path names
1154  $src = $this->davDeslashify($options['path']);
1155  $srcParent = dirname($src);
1156  $srcName = $this->davBasename($src);
1157  $dst = $this->davDeslashify($options['dest']);
1158 
1159  $dstParent = dirname($dst);
1160  $dstName = $this->davBasename($dst);
1161  $this->writelog('move '.$dst.' dstname='.$dstName);
1162  // Source and destination must not be the same
1163  if ($src == $dst)
1164  {
1165  return '409 Conflict (source and destination are the same)';
1166  }
1167 
1168  // Destination must not be in a subtree of source
1169  if (substr($dst,strlen($src)+1) == $src.'/')
1170  {
1171  return '409 Conflict (destination is in subtree of source)';
1172  }
1173 
1174  // Get dav objects for path
1175  $srcDAV =& $this->getObject($src);
1176  $dstDAV =& $this->getObject($dst);
1177  $srcParentDAV =& $this->getObject($srcParent);
1178  $dstParentDAV =& $this->getObject($dstParent);
1179 
1180  // Source must exist
1181  if ($srcDAV == null)
1182  {
1183  return '409 Conflict (source does not exist)';
1184  }
1185 
1186  // Overwriting is only allowed, if overwrite option is set to 'T'
1187  $isOverwritten = false;
1188  if ($dstDAV != null)
1189  {
1190  if ($options['overwrite'] == 'T')
1191  {
1192  // Delete the overwritten destination
1193  if ($dstDAV->isPermitted('delete'))
1194  {
1195  $dstParentDAV->remove($dstDAV);
1196  $dstDAV = null;
1197  $isOverwritten = true;
1198  } else {
1199  return '403 Not Permitted';
1200  }
1201  } else {
1202  return '412 Precondition Failed';
1203  }
1204  }
1205 
1206  // Parents of destination must exist
1207  if ($dstParentDAV == null)
1208  {
1209  return '409 Conflict (parent of destination does not exist)';
1210  }
1211 
1212  if ($srcParent == $dstParent)
1213  {
1214  // Rename source, if source and dest are in same parent
1215 
1216  // Check permission
1217  if (! $srcDAV->isPermitted('write'))
1218  {
1219  return '403 Forbidden';
1220  }
1221  $this->writelog('rename dstName='.$dstName);
1222  $srcDAV->setResourceName($dstName);
1223  $srcDAV->write();
1224  } else {
1225  // Move source, if source and dest are in same parent
1226 
1227 
1228  if (! $srcDAV->isPermitted('delete'))
1229  {
1230  return '403 Forbidden';
1231  }
1232 
1233  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1234  {
1235  return '403 Forbidden';
1236  }
1237  $dstParentDAV->addMove($srcDAV, $dstName);
1238  }
1239 
1240  // Record write event
1241  if ($isOverwritten)
1242  {
1243  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1244  }
1245  else
1246  {
1247  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1248  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1249  }
1250 
1251  return ($isOverwritten) ? '204 No Content' : '201 Created';
1252  }
1253 
1260  public function COPY($options, $del=false)
1261  {
1262  global $ilUser;
1263  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1264  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1265 
1266  // no copying to different WebDAV Servers
1267  if (isset($options["dest_url"])) {
1268  return "502 bad gateway";
1269  }
1270 
1271  $src = $this->davDeslashify($options['path']);
1272  $srcParent = dirname($src);
1273  $srcName = $this->davBasename($src);
1274  $dst = $this->davDeslashify($options['dest']);
1275  $dstParent = dirname($dst);
1276  $dstName = $this->davBasename($dst);
1277 
1278  // sanity check
1279  if ($src == $dst)
1280  {
1281  return '409 Conflict'; // src and dst are the same
1282  }
1283 
1284  if (substr($dst,strlen($src)+1) == $src.'/')
1285  {
1286  return '409 Conflict'; // dst is in subtree of src
1287  }
1288 
1289  $this->writelog('COPY src='.$src.' dst='.$dst);
1290  // get dav object for path
1291  $srcDAV =& $this->getObject($src);
1292  $dstDAV =& $this->getObject($dst);
1293  $dstParentDAV =& $this->getObject($dstParent);
1294 
1295  if (is_null($srcDAV) || $srcDAV->isNullResource())
1296  {
1297  return '409 Conflict'; // src does not exist
1298  }
1299  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1300  {
1301  return '409 Conflict'; // parent of dst does not exist
1302  }
1303  $isOverwritten = false;
1304 
1305  // XXX Handle nulltype for dstDAV
1306  if (! is_null($dstDAV))
1307  {
1308  if ($options['overwrite'] == 'T')
1309  {
1310  if ($dstDAV->isPermitted('delete'))
1311  {
1312  $dstParentDAV->remove($dstDAV);
1313  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1314 
1315  $dstDAV = null;
1316  $isOverwritten = true;
1317  } else {
1318  return '403 Forbidden';
1319  }
1320  } else {
1321  return '412 Precondition Failed';
1322  }
1323  }
1324 
1325  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1326  {
1327  return '403 Forbidden';
1328  }
1329  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1330 
1331  // Record write event
1332  ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1333  $srcDAV->getObjectId(), $ilUser->getId());
1334  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1335 
1336  return ($isOverwritten) ? '204 No Content' : '201 Created';
1337  }
1338 
1345  public function PROPPATCH(&$options)
1346  {
1347  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1348  $this->writelog('PROPPATCH '.$options['path']);
1349 
1350  // get dav object for path
1351  $path =& $this->davDeslashify($options['path']);
1352  $objDAV =& $this->getObject($path);
1353 
1354  // sanity check
1355  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1356 
1357  $isPermitted = $objDAV->isPermitted('write');
1358  foreach($options['props'] as $key => $prop) {
1359  if (!$isPermitted || $prop['ns'] == 'DAV:')
1360  {
1361  $options['props'][$key]['status'] = '403 Forbidden';
1362  } else {
1363  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1364  }
1365  }
1366 
1367  return "";
1368  }
1369 
1370 
1377  public function LOCK(&$options)
1378  {
1379  global $ilias;
1380  $this->writelog('LOCK('.var_export($options, true).')');
1381  $this->writelog('LOCK '.$options['path']);
1382 
1383  // Check if an object with the path exists.
1384  $path =& $this->davDeslashify($options['path']);
1385  $objDAV =& $this->getObject($path);
1386  // Handle null-object locking
1387  // --------------------------
1388  if (is_null($objDAV))
1389  {
1390  $this->writelog('LOCK handling null-object locking...');
1391 
1392  // If the name does not exist, we create a null-object for it
1393  if (isset($options["update"]))
1394  {
1395  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1396  return '412 Precondition Failed';
1397  }
1398 
1399  $parent = dirname($path);
1400  $parentDAV =& $this->getObject($parent);
1401  if (is_null($parentDAV))
1402  {
1403  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1404  return '404 Not Found';
1405  }
1406  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1407  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1408  {
1409  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1410  return '403 Forbidden';
1411  }
1412 
1413  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1414  $this->writelog('created null resource for '.$path);
1415  }
1416 
1417  // ---------------------
1418  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1419  {
1420  $this->writelog('LOCK lock failed - user has no write permission.');
1421  return '403 Forbidden';
1422  }
1423 
1424  // XXX - Check if there are other locks on the resource
1425  if (!isset($options['timeout']) || is_array($options['timeout']))
1426  {
1427  $options["timeout"] = time()+360; // 6min.
1428  }
1429 
1430  if(isset($options["update"])) { // Lock Update
1431  $this->writelog('LOCK update token='.var_export($options,true));
1432  $success = $this->locks->updateLockWithoutCheckingDAV(
1433  $objDAV,
1434  $options['update'],
1435  $options['timeout']
1436  );
1437  if ($success)
1438  {
1439  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1440  if ($data['ilias_owner'] == $ilias->account->getId())
1441  {
1442  $owner = $data['dav_owner'];
1443  } else {
1444  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1445  }
1446  $options['owner'] = $owner;
1447  $options['locktoken'] = $data['token'];
1448  $options['timeout'] = $data['expires'];
1449  $options['depth'] = $data['depth'];
1450  $options['scope'] = $data['scope'];
1451  $options['type'] = $data['scope'];
1452  }
1453 
1454  } else {
1455  $this->writelog('LOCK create new lock');
1456 
1457  // XXX - Attempting to create a recursive exclusive lock
1458  // on a collection must fail, if any of nodes in the subtree
1459  // of the collection already has a lock.
1460  // XXX - Attempting to create a recursive shared lock
1461  // on a collection must fail, if any of nodes in the subtree
1462  // of the collection already has an exclusive lock.
1463  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1464  $this->writelog('lock owner='.$owner);
1465  $success = $this->locks->lockWithoutCheckingDAV(
1466  $objDAV,
1467  $ilias->account->getId(),
1468  trim($options['owner']),
1469  $options['locktoken'],
1470  $options['timeout'],
1471  $options['depth'],
1472  $options['scope']
1473  );
1474  }
1475 
1476  // Note: As a workaround for the Microsoft WebDAV Client, we return
1477  // true/false here (resulting in the status '200 OK') instead of
1478  // '204 No Content').
1479  //return ($success) ? '204 No Content' : false;
1480  return $success;
1481  }
1482 
1489  public function UNLOCK(&$options)
1490  {
1491  global $log, $ilias;
1492  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1493  $this->writelog('UNLOCK '.$options['path']);
1494 
1495  // Check if an object with the path exists.
1496  $path =& $this->davDeslashify($options['path']);
1497  $objDAV =& $this->getObject($path);
1498  if (is_null($objDAV)) {
1499  return '404 Not Found';
1500  }
1501  if (! $objDAV->isPermitted('write')) {
1502  return '403 Forbidden';
1503  }
1504 
1505  $success = $this->locks->unlockWithoutCheckingDAV(
1506  $objDAV,
1507  $options['token']
1508  );
1509 
1510  // Delete null resource object if there are no locks associated to
1511  // it anymore
1512  if ($objDAV->isNullResource()
1513  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1514  {
1515  $parent = dirname($this->davDeslashify($options['path']));
1516  $parentDAV =& $this->getObject($parent);
1517  $parentDAV->remove($objDAV);
1518  }
1519 
1520  // Workaround for Mac OS X: We must return 200 here instead of
1521  // 204.
1522  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1523  return ($success) ? '200 OK' : '412 Precondition Failed';
1524  }
1525 
1538  protected function checkLock($path)
1539  {
1540  global $ilias;
1541 
1542  $this->writelog('checkLock('.$path.')');
1543  $result = null;
1544 
1545  // get dav object for path
1546  //$objDAV = $this->getObject($path);
1547 
1548  // convert DAV path into ilObjectDAV path
1549  $objPath = $this->toObjectPath($path);
1550  if (! is_null($objPath))
1551  {
1552  $objDAV = $objPath[count($objPath) - 1];
1553  $locks = $this->locks->getLocksOnPathDAV($objPath);
1554  foreach ($locks as $lock)
1555  {
1556  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1557  && $lock['node_id'] == $objDAV->getNodeId();
1558 
1559  // Check all locks on last object in path,
1560  // but only locks with depth infinity on parent objects.
1561  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1562  {
1563  // DAV Clients expects to see their own owner name in
1564  // the locks. Since these names are not unique (they may
1565  // just be the name of the local user running the DAV client)
1566  // we return the ILIAS user name in all other cases.
1567  if ($lock['ilias_owner'] == $ilias->account->getId())
1568  {
1569  $owner = $lock['dav_owner'];
1570  } else {
1571  $owner = $this->getLogin($lock['ilias_owner']);
1572  }
1573 
1574  // FIXME - Shouldn't we collect all locks instead of
1575  // using an arbitrary one?
1576  $result = array(
1577  "type" => "write",
1578  "obj_id" => $lock['obj_id'],
1579  "node_id" => $lock['node_id'],
1580  "scope" => $lock['scope'],
1581  "depth" => $lock['depth'],
1582  "owner" => $owner,
1583  "token" => $lock['token'],
1584  "expires" => $lock['expires']
1585  );
1586  if ($lock['scope'] == 'exclusive')
1587  {
1588  // If there is an exclusive lock in the path, it
1589  // takes precedence over all non-exclusive locks in
1590  // parent nodes. Therefore we can can finish collecting
1591  // locks.
1592  break;
1593  }
1594  }
1595  }
1596  }
1597  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1598 
1599  return $result;
1600  }
1601 
1606  protected function getLogin($userId)
1607  {
1608  $login = ilObjUser::_lookupLogin($userId);
1609  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1610  return $login;
1611  }
1612 
1613 
1620  private function getObject($davPath)
1621  {
1622  global $tree;
1623 
1624 
1625  // If the second path elements starts with 'file_', the following
1626  // characters of the path element directly identify the ref_id of
1627  // a file object.
1628  $davPathComponents = split('/',substr($davPath,1));
1629  if (count($davPathComponents) > 1 &&
1630  substr($davPathComponents[1],0,5) == 'file_')
1631  {
1632  $ref_id = substr($davPathComponents[1],5);
1633  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1634 
1635  // Poor IE needs this, in order to successfully display
1636  // PDF documents
1637  header('Pragma: private');
1638  }
1639  else
1640  {
1641  $nodePath = $this->toNodePath($davPath);
1642  if ($nodePath == null && count($davPathComponents) == 1)
1643  {
1644  return ilObjectDAV::createObject(-1,'mountPoint');
1645  }
1646  }
1647  if (is_null($nodePath))
1648  {
1649  return null;
1650  } else {
1651  $top = $nodePath[count($nodePath) - 1];
1652  return ilObjectDAV::createObject($top['child'],$top['type']);
1653  }
1654  }
1661  private function toObjectPath($davPath)
1662  {
1663  $this->writelog('toObjectPath('.$davPath);
1664  global $tree;
1665 
1666  $nodePath = $this->toNodePath($davPath);
1667 
1668  if (is_null($nodePath))
1669  {
1670  return null;
1671  } else {
1672  $objectPath = array();
1673  foreach ($nodePath as $node)
1674  {
1675  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1676  if (is_null($pathElement))
1677  {
1678  break;
1679  }
1680  $objectPath[] = $pathElement;
1681  }
1682  return $objectPath;
1683  }
1684  }
1685 
1697  public function toNodePath($davPath)
1698  {
1699  global $tree;
1700  $this->writelog('toNodePath('.$davPath.')...');
1701 
1702  // Split the davPath into path titles
1703  $titlePath = split('/',substr($davPath,1));
1704 
1705  // Remove the client id from the beginning of the title path
1706  if (count($titlePath) > 0)
1707  {
1708  array_shift($titlePath);
1709  }
1710 
1711  // If the last path title is empty, remove it
1712  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1713  {
1714  array_pop($titlePath);
1715  }
1716 
1717  // If the path is empty, return null
1718  if (count($titlePath) == 0)
1719  {
1720  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1721  return null;
1722  }
1723 
1724  // If the path is an absolute path, ref_id is null.
1725  $ref_id = null;
1726 
1727  // If the path is a relative folder path, convert it into an absolute path
1728  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1729  {
1730  $ref_id = substr($titlePath[0],4);
1731  array_shift($titlePath);
1732  }
1733 
1734  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1735 
1736  $this->writelog('toNodePath():'.var_export($nodePath,true));
1737  return $nodePath;
1738  }
1739 
1746  private function davDeslashify($path)
1747  {
1749 
1750  if ($path[strlen($path)-1] == '/') {
1751  $path = substr($path,0, strlen($path) - 1);
1752  }
1753  $this->writelog('davDeslashify:'.$path);
1754  return $path;
1755  }
1756 
1763  private function davBasename($path)
1764  {
1765  $components = split('/',$path);
1766  return count($components) == 0 ? '' : $components[count($components) - 1];
1767  }
1768 
1775  protected function writelog($message)
1776  {
1777  // Only write log message, if we are in debug mode
1778  if ($this->isDebug)
1779  {
1780  global $ilLog, $ilias;
1781  if ($ilLog)
1782  {
1783  if ($message == '---')
1784  {
1785  $ilLog->write('');
1786  } else {
1787  $ilLog->write(
1788  $ilias->account->getLogin()
1789  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1790  .' ilDAVServer.'.str_replace("\n",";",$message)
1791  );
1792  }
1793  }
1794  else
1795  {
1796  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1797  fwrite($fh, date('Y-m-d H:i:s '));
1798  fwrite($fh, str_replace("\n",";",$message));
1799  fwrite($fh, "\n\n");
1800  fclose($fh);
1801  }
1802  }
1803  }
1804 
1817  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1818  {
1819  if ($genericURI) {
1820  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1821  $query = null;
1822  } else if ($this->clientOS == 'windows') {
1823  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1824  $query = 'mount-instructions';
1825  } else if ($this->clientBrowser == 'konqueror') {
1826  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1827  $query = null;
1828  } else if ($this->clientBrowser == 'nautilus') {
1829  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1830  $query = null;
1831  } else {
1832  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1833  $query = 'mount-instructions';
1834  }
1835  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1836  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1837 
1838  $uri = $baseUri.'/ref_'.$refId.'/';
1839  if ($query != null)
1840  {
1841  $uri .= '?'.$query;
1842  }
1843 
1844  return $uri;
1845  }
1860  function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1861  {
1862  if ($this->clientOS == 'windows') {
1863  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1864  $query = null;
1865  } else if ($this->clientBrowser == 'konqueror') {
1866  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1867  $query = null;
1868  } else if ($this->clientBrowser == 'nautilus') {
1869  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1870  $query = null;
1871  } else {
1872  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1873  $query = null;
1874  }
1875  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1876  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1877 
1878  $uri = $baseUri.'/ref_'.$refId.'/';
1879  if ($query != null)
1880  {
1881  $uri .= '?'.$query;
1882  }
1883 
1884  return $uri;
1885  }
1897  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1898  {
1899  $nodeId = 0;
1900  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1901  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1902  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1903 
1904  if (! is_null($ressourceName) && ! is_null($parentRefId))
1905  {
1906  // Quickly create URI from the known data without needing SQL queries
1907  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1908  } else {
1909  // Create URI and use some SQL queries to get the missing data
1910  global $tree;
1911  $nodePath = $tree->getNodePath($refId);
1912 
1913  if (is_null($nodePath) || count($nodePath) < 2)
1914  {
1915  // No object path? Return null - file is not in repository.
1916  $uri = null;
1917  } else {
1918  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1919  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1920  }
1921  }
1922  return $uri;
1923  }
1924 
1942  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1943  {
1944  $nodeId = 0;
1945  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1946  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1947  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1948 
1949  if (! is_null($ressourceName) && ! is_null($parentRefId))
1950  {
1951  // Quickly create URI from the known data without needing SQL queries
1952  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1953  } else {
1954  // Create URI and use some SQL queries to get the missing data
1955  global $tree;
1956  $nodePath = $tree->getNodePath($refId);
1957 
1958  if (is_null($nodePath) || count($nodePath) < 2)
1959  {
1960  // No object path? Return null - file is not in repository.
1961  $uri = null;
1962  } else {
1963  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1964  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1965  }
1966  }
1967  return $uri;
1968  }
1969 
1975  public function isWebDAVoverHTTPS() {
1976  if ($this->isHTTPS == null) {
1977  global $ilSetting;
1978  require_once './Services/Http/classes/class.ilHTTPS.php';
1979  $https = new ilHTTPS();
1980  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1981  }
1982  return $this->isHTTPS;
1983  }
1984 
1993  public static function _isActive()
1994  {
1995  global $ilClientIniFile;
1996  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1997  @include_once("Auth/HTTP.php");
1998  }
2004  public static function _isActionsVisible()
2005  {
2006  global $ilClientIniFile;
2007  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
2008  }
2009 
2019  public static function _getDefaultWebfolderInstructions()
2020  {
2021  global $lng;
2022  return $lng->txt('webfolder_instructions_text');
2023  }
2024 
2051  public static function _getWebfolderInstructionsFor($webfolderTitle,
2052  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2053  $os = 'unknown', $osFlavor = 'unknown')
2054  {
2055  global $ilSetting;
2056 
2057  $settings = new ilSetting('file_access');
2058  $str = $settings->get('custom_webfolder_instructions', '');
2059  if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2060  {
2062  }
2063  if(is_file('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm')){
2064  $str = fread(fopen('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm', "rb"),filesize('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm'));
2065  }
2066  $str=utf8_encode($str);
2067 
2068  preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2069  $refID=$matches[0][0];
2070 
2071  $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2072  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2073  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2074  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2075  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2076  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2077  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2078 
2079  if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false){
2080  $str = preg_replace('/\[IF_IEXPLORE\]((?:.|\n)*)\[\/IF_IEXPLORE\]/','\1', $str);
2081  }else{
2082  $str = preg_replace('/\[IF_NOTIEXPLORE\]((?:.|\n)*)\[\/IF_NOTIEXPLORE\]/','\1', $str);
2083  }
2084 
2085  switch ($os)
2086  {
2087  case 'windows' :
2088  $operatingSystem = 'WINDOWS';
2089  break;
2090  case 'unix' :
2091  switch ($osFlavor)
2092  {
2093  case 'osx' :
2094  $operatingSystem = 'MAC';
2095  break;
2096  case 'linux' :
2097  $operatingSystem = 'LINUX';
2098  break;
2099  default :
2100  $operatingSystem = 'LINUX';
2101  break;
2102  }
2103  break;
2104  default :
2105  $operatingSystem = 'UNKNOWN';
2106  break;
2107  }
2108 
2109  if ($operatingSystem != 'UNKNOWN')
2110  {
2111  $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2112  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2113  }
2114  else
2115  {
2116  $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2117  }
2118  return $str;
2119  }
2120 
2126  private function getUploadMaxFilesize() {
2127  $val = ini_get('upload_max_filesize');
2128 
2129  $val = trim($val);
2130  $last = strtolower($val[strlen($val)-1]);
2131  switch($last) {
2132  // The 'G' modifier is available since PHP 5.1.0
2133  case 'g':
2134  $val *= 1024;
2135  case 'm':
2136  $val *= 1024;
2137  case 'k':
2138  $val *= 1024;
2139  }
2140 
2141  return $val;
2142  }
2143 }
2144 // END WebDAV
2145 ?>
static _isActionsVisible()
Static getter.
static _lookupLogin($a_user_id)
lookup login
$clientBrowser
The name of some well known browsers, that need special support.
ILIAS Setting Class.
exit
Definition: login.php:54
createObject($refId, $type)
Static factory method to create a DAV object for a given refId and type.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
$result
$_GET["client_id"]
static lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=false)
$isDebug
The WebDAVServer prints lots of log messages to the ilias log, if this variable is set to true...
DELETE($options)
DELETE method handler.
MOVE($options)
MOVE method handler.
showMountInstructions(&$objDAV, &$options)
Mount instructions method handler for directories.
mountDir(&$objDAV, &$options)
Mount method handler for directories.
getFileURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a file object using WebDAV.
static _getDefaultWebfolderInstructions()
Gets instructions for the usage of webfolders.
static _isActive()
Static getter.
Virtual base class for implementing WebDAV servers.
Definition: Server.php:23
COPY($options, $del=false)
COPY method handler.
LOCK(&$options)
LOCK method handler.
ilDAVServer()
Constructor.
toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:157
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1924
checkLock($path)
checkLock() helper
GET(&$options)
GET method handler.
PUTfinished(&$options)
PUTfinished handler.
$clientOSFlavor
The flavor of the operating system of the WebDAV client.
$info
Definition: example_052.php:80
serveRequest()
Serves a WebDAV request.
HTTPS.
$cachedObjectDAV
Cached object handler.
$data
global $https
Definition: imgupload.php:15
$clientOS
The operating system of the WebDAV client.
$success
Definition: Utf8Test.php:87
writelog($message)
Writes a message to the logfile.,.
if(!is_array($argv)) $options
getLogin($userId)
Returns the login for the specified user id, or null if the user does not exist.
PROPFIND(&$options, &$files)
PROPFIND method handler.
isFileHidden(&$objDAV)
Returns true, if the resource has a file name which is hidden from the user.
mkprop()
helper for property element creation
Definition: Server.php:1551
$locks
Handler for locks.
getObjectURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a object using WebDAV by its name.
HTTP_WebDAV_Server()
Constructor.
Definition: Server.php:108
fileinfo($resourcePath, $displayPath, &$objDAV)
Creates file info properties for a single file/resource.
static getInstance()
Get singelton iunstance.
MKCOL($options)
MKCOL method handler.
_recordReadEvent($a_type, $a_ref_id, $obj_id, $usr_id, $isCatchupWriteEvents=true, $a_ext_rc=false, $a_ext_time=false)
Records a read event and catches up with write events.
davBasename($path)
Private implementation of PHP basename() function.
davUrlEncode($path)
We do not implement this method, because authentication is done by ilias3/webdav.php.
PUT(&$options)
PUT method handler.
getDir(&$objDAV, &$options)
GET method handler for directories.
PROPPATCH(&$options)
PROPPATCH method handler.
global $ilUser
Definition: imgupload.php:15
$ref_id
Definition: sahs_server.php:39
toNodePath($davPath)
Converts a DAV path into a node path.
global $ilSetting
Definition: privfeed.php:40
global $lng
Definition: privfeed.php:40
$properties
Handler for properties.
$GLOBALS['PHPCAS_CLIENT']
This global variable is used by the interface class phpCAS.
Definition: CAS.php:276
davDeslashify($path)
davDeslashify - make sure path does not end in a slash
_catchupWriteEvents($obj_id, $usr_id, $timestamp=null)
Catches up with all write events which occured before the specified timestamp.
isWebDAVoverHTTPS()
Returns true, if the WebDAV server transfers data over HTTPS.
toObjectPath($davPath)
Converts a DAV path into an array of DAV objects.
getFolderURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null)
Returns an URI for mounting the repository object as a webfolder using Internet Explorer and Firefox ...
static _getWebfolderInstructionsFor($webfolderTitle, $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus, $os='unknown', $osFlavor='unknown')
Gets Webfolder mount instructions for the specified webfolder.
static getValidFilename($a_filename)
Get valid filename.
_recordWriteEvent($obj_id, $usr_id, $action, $parent_obj_id=null)
Records a write event.
getMountURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null, $genericURI=false)
Returns an URI for mounting the repository object as a webfolder.
getUploadMaxFilesize()
Gets the maximum permitted upload filesize from php.ini in bytes.
UNLOCK(&$options)
UNLOCK method handler.
getObject($davPath)
Gets a DAV object for the specified path.