ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
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('include/Unicode/UtfNormal.php');
32 require_once('Services/Tracking/classes/class.ilChangeEvent.php');
33 
51 {
56  private static $instance = null;
57 
63 
67  private $locks;
71  private $properties;
72 
78  private $clientOS = 'unknown';
83  private $clientOSFlavor = 'unknown';
88  private $clientBrowser = 'unknown';
89 
100  private $putObjDAV = null;
101 
108  private $isHTTPS = null;
109 
114  private $isDebug = false;
115 
125  public function ilDAVServer()
126  {
127  $this->writelog("<constructor>");
128 
129  // Initialize the WebDAV server and create
130  // locking and property support objects
131  $this->HTTP_WebDAV_Server();
132  $this->locks = new ilDAVLocks();
133  $this->properties = new ilDAVProperties();
134  //$this->locks->createTable();
135  //$this->properties->createTable();
136 
137  // Guess operating system, operating system flavor and browser of the webdav client
138  //
139  // - We need to know the operating system in order to properly
140  // hide hidden resources in directory listings.
141  //
142  // - We need the operating system flavor and the browser to
143  // properly support mounting of a webdav folder.
144  //
145  $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
146  $this->writelog('userAgent='.$userAgent);
147  if (strpos($userAgent,'windows') !== false
148  || strpos($userAgent,'microsoft') !== false)
149  {
150  $this->clientOS = 'windows';
151  if(strpos($userAgent,'nt 5.1') !== false){
152  $this->clientOSFlavor = 'xp';
153  }else{
154  $this->clientOSFlavor = 'nichtxp';
155  }
156 
157  } else if (strpos($userAgent,'darwin') !== false
158  || strpos($userAgent,'macintosh') !== false
159  || strpos($userAgent,'linux') !== false
160  || strpos($userAgent,'solaris') !== false
161  || strpos($userAgent,'aix') !== false
162  || strpos($userAgent,'unix') !== false
163  || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
164  )
165  {
166  $this->clientOS = 'unix';
167  if (strpos($userAgent,'linux') !== false)
168  {
169  $this->clientOSFlavor = 'linux';
170  }
171  else if (strpos($userAgent,'macintosh') !== false)
172  {
173  $this->clientOSFlavor = 'osx';
174  }
175  }
176  if (strpos($userAgent,'konqueror') !== false)
177  {
178  $this->clientBrowser = 'konqueror';
179  }
180  }
181 
186  public static function getInstance()
187  {
188  if(self::$instance != NULL)
189  {
190  return self::$instance;
191  }
192  return self::$instance = new ilDAVServer();
193  }
194 
198  public function serveRequest()
199  {
200  // die quickly if plugin is deactivated
201  if (!self::_isActive())
202  {
203  $this->writelog(__METHOD__.' WebDAV disabled. Aborting');
204  $this->http_status('403 Forbidden');
205  echo '<html><body><h1>Sorry</h1>'.
206  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
207  '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
208  '</body></html>';
209  return;
210  }
211 
212  try {
213  $start = time();
214  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
216  $end = time();
217  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
218  $this->writelog('---');
219  }
220  catch (Exception $e)
221  {
222  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
223  }
224  }
225 
260  private function davUrlEncode($path)
261  {
262  // We compose the path to Unicode Normal Form NFC
263  // This ensures that diaeresis and other special characters
264  // are treated uniformly on Windows and on Mac OS X
266 
267  $c = explode('/',$path);
268  for ($i = 0; $i < count($c); $i++)
269  {
270  $c[$i] = str_replace('+','%20',urlencode($c[$i]));
271  }
272  return implode('/',$c);
273  }
274 
282  public function PROPFIND(&$options, &$files)
283  {
284  // Activate tree cache
285  global $tree;
286  //$tree->useCache(true);
287 
288  $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
289  $this->writelog('PROPFIND '.$options['path']);
290 
291  // get dav object for path
292  $path =& $this->davDeslashify($options['path']);
293  $objDAV =& $this->getObject($path);
294 
295  // prepare property array
296  $files['files'] = array();
297 
298  // sanity check
299  if (is_null($objDAV)) {
300  return false;
301  }
302  if (! $objDAV->isPermitted('visible,read')) {
303  return '403 Forbidden';
304  }
305 
306  // store information for the requested path itself
307  // FIXME : create display name for object.
308  $encodedPath = $this->davUrlEncode($path);
309 
310  $GLOBALS['ilLog']->write(print_r($encodedPath,true));
311 
312  $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
313 
314  // information for contained resources requested?
315  if (!empty($options['depth'])) {
316  // The breadthFirst list holds the collections which we have not
317  // processed yet. If depth is infinity we append unprocessed collections
318  // to the end of this list, and remove processed collections from
319  // the beginning of this list.
320  $breadthFirst = array($objDAV);
321  $objDAV->encodedPath = $encodedPath;
322 
323  while (count($breadthFirst) > 0) {
324  // remove a collection from the beginning of the breadthFirst list
325  $collectionDAV = array_shift($breadthFirst);
326  $childrenDAV =& $collectionDAV->childrenWithPermission('visible,read');
327  foreach ($childrenDAV as $childDAV)
328  {
329  // On duplicate names, work with the older object (the one with the
330  // smaller object id).
331  foreach ($childrenDAV as $duplChildDAV)
332  {
333  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
334  $duplChildDAV->getResourceName() == $childDAV->getResourceName())
335  {
336  continue 2;
337  }
338  }
339 
340  // only add visible objects to the file list
341  if (!$this->isFileHidden($childDAV))
342  {
343  $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
344  $files['files'][] =& $this->fileinfo(
345  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
346  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
347  $childDAV
348  );
349  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
350  // add a collection to the end of the breadthFirst list
351  $breadthFirst[] = $childDAV;
352  $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
353  }
354  }
355  }
356  }
357  }
358 
359  // Record read event but don't catch up with write events, because
360  // with WebDAV, a user can not see all objects contained in a folder.
361  global $ilUser;
362  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
363  $objDAV->getObjectId(), $ilUser->getId(), false);
364 
365  // ok, all done
366  $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
367  return true;
368  }
369 
380  private function isFileHidden(&$objDAV)
381  {
382  // Hide null resources which haven't got an active lock
383  if ($objDAV->isNullResource()) {
384  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
385  return;
386  }
387  }
388 
389  $name = $objDAV->getResourceName();
390  $isFileHidden = false;
391  switch ($this->clientOS)
392  {
393  case 'unix' :
394  // Hide Windows thumbnail files, and files which start with '~$'.
395  $isFileHidden =
396  $name == 'Thumbs.db'
397  || substr($name, 0, 2) == '~$';
398  // Hide files which contain /
399  $isFileHidden |= preg_match('/\\//', $name);
400  break;
401  case 'windows' :
402  // Hide files that start with '.'.
403  $isFileHidden = substr($name, 0, 1) == '.';
404  // Hide files which contain \ / : * ? " < > |
405  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
406  break;
407  default :
408  // Hide files which contain /
409  $isFileHidden |= preg_match('/\\//', $name);
410  break;
411  }
412  $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
413  return $isFileHidden;
414  }
415 
423  private function fileinfo($resourcePath, $displayPath, &$objDAV)
424  {
425  global $ilias;
426 
427  $this->writelog('fileinfo('.$resourcePath.')');
428  // create result array
429  $info = array();
430  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
431  * end of a resource path to a collection.
432  * However Mac OS X does not like this!
433  */
434  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
435  $info['path'] = $resourcePath.'/';
436  } else {
437  $info['path'] = $resourcePath;
438  }
439 
440  $info['props'] = array();
441 
442  // no special beautified displayname here ...
443  $info["props"][] =& $this->mkprop("displayname", $displayPath);
444 
445  // creation and modification time
446  $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
447  $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
448 
449  // directory (WebDAV collection)
450  $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
451  $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
452  $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
453 
454  // Only show supported locks for users who have write permission
455  if ($objDAV->isPermitted('write'))
456  {
457  $info["props"][] =& $this->mkprop("supportedlock",
458  '<D:lockentry>'
459  .'<D:lockscope><D:exclusive/></D:lockscope>'
460  .'<D:locktype><D:write/></D:locktype>'
461  .'</D:lockentry>'
462  .'<D:lockentry>'
463  .'<D:lockscope><D:shared/></D:lockscope>'
464  .'<D:locktype><D:write/></D:locktype>'
465  .'</D:lockentry>'
466  );
467  }
468 
469  // Maybe we should only show locks on objects for users who have write permission.
470  // But if we don't show these locks, users who have write permission in an object
471  // further down in a hierarchy can't see who is locking their object.
472  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
473  $lockdiscovery = '';
474  foreach ($locks as $lock)
475  {
476  // DAV Clients expects to see their own owner name in
477  // the locks. Since these names are not unique (they may
478  // just be the name of the local user running the DAV client)
479  // we return the ILIAS user name in all other cases.
480  if ($lock['ilias_owner'] == $ilias->account->getId())
481  {
482  $owner = $lock['dav_owner'];
483  } else {
484  $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
485  }
486  $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
487 
488  $lockdiscovery .=
489  '<D:activelock>'
490  .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
491  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
492  .'<D:locktype><D:write/></D:locktype>'
493  .'<D:depth>'.$lock['depth'].'</D:depth>'
494  .'<D:owner>'.$owner.'</D:owner>'
495 
496  // more than a million is considered an absolute timestamp
497  // less is more likely a relative value
498  .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
499  .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
500  .'</D:activelock>'
501  ;
502  }
503  if (strlen($lockdiscovery) > 0)
504  {
505  $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
506  }
507 
508  // get additional properties from database
509  $properties = $this->properties->getAll($objDAV);
510  foreach ($properties as $prop)
511  {
512  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
513  }
514 
515  //$this->writelog('fileinfo():'.var_export($info, true));
516  return $info;
517  }
518 
530  public function GET(&$options)
531  {
532  global $ilUser;
533 
534  $this->writelog('GET('.var_export($options, true).')');
535  $this->writelog('GET('.$options['path'].')');
536 
537  // get dav object for path
538  $path = $this->davDeslashify($options['path']);
539  $objDAV =& $this->getObject($path);
540 
541  // sanity check
542  if (is_null($objDAV) || $objDAV->isNullResource())
543  {
544  return false;
545  }
546 
547  if (! $objDAV->isPermitted('visible,read'))
548  {
549  return '403 Forbidden';
550  }
551 
552  // is this a collection?
553  if ($objDAV->isCollection())
554  {
555  if (isset($_GET['mount']))
556  {
557  return $this->mountDir($objDAV, $options);
558  }
559  else if (isset($_GET['mount-instructions']))
560  {
561  return $this->showMountInstructions($objDAV, $options);
562  }
563  else
564  {
565  return $this->getDir($objDAV, $options);
566  }
567  }
568  // detect content type
569  $options['mimetype'] =& $objDAV->getContentType();
570  // detect modification time
571  // see rfc2518, section 13.7
572  // some clients seem to treat this as a reverse rule
573  // requiring a Last-Modified header if the getlastmodified header was set
574  $options['mtime'] =& $objDAV->getModificationTimestamp();
575 
576  // detect content length
577  $options['size'] =& $objDAV->getContentLength();
578 
579  // get content as stream or as data array
580  $options['stream'] =& $objDAV->getContentStream();
581  if (is_null($options['stream']))
582  {
583  $options['data'] =& $objDAV->getContentData();
584  }
585 
586  // Record read event and catch up write events
587  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
588  $objDAV->getObjectId(), $ilUser->getId());
589 
590  $this->writelog('GET:'.var_export($options, true));
591 
592  return true;
593  }
605  private function mountDir(&$objDAV, &$options)
606  {
607  $path = $this->davDeslashify($options['path']);
608 
609  header('Content-Type: application/davmount+xml');
610 
611  echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
612  echo " </dm:url>".$this->base_uri."</dm:url>\n";
613 
614  $xmlPath = str_replace('&','&amp;',$path);
615  $xmlPath = str_replace('<','&lt;',$xmlPath);
616  $xmlPath = str_replace('>','&gt;',$xmlPath);
617 
618  echo " </dm:open>$xmlPath</dm:open>\n";
619  echo "</dm:mount>\n";
620 
621  exit;
622 
623  }
630  private function showMountInstructions(&$objDAV, &$options)
631  {
632  global $lng,$ilUser;
633 
634  $path = $this->davDeslashify($options['path']);
635 
636  // The $path variable may contain a full or a shortened DAV path.
637  // We convert it into an object path, which we can then use to
638  // construct a new full DAV path.
639  $objectPath = $this->toObjectPath($path);
640 
641  // Construct a (possibly) full DAV path from the object path.
642  $fullPath = '';
643  foreach ($objectPath as $object)
644  {
645  if ($object->getRefId() == 1 && $this->isFileHidden($object))
646  {
647  // If the repository root object is hidden, we can not
648  // create a full path, because nothing would appear in the
649  // webfolder. We resort to a shortened path instead.
650  $fullPath .= '/ref_1';
651  }
652  else
653  {
654  $fullPath .= '/'.$this->davUrlEncode($object->getResourceName());
655  }
656  }
657 
658  // Construct a shortened DAV path from the object path.
659  $shortenedPath = '/ref_'.
660  $objectPath[count($objectPath) - 1]->getRefId();
661 
662  if ($objDAV->isCollection())
663  {
664  $shortenedPath .= '/';
665  $fullPath .= '/';
666  }
667 
668  // Prepend client id to path
669  $shortenedPath = '/'.CLIENT_ID.$shortenedPath;
670  $fullPath = '/'.CLIENT_ID.$fullPath;
671 
672  // Construct webfolder URI's. The URI's are used for mounting the
673  // webfolder. Since mounting using URI's is not standardized, we have
674  // to create different URI's for different browsers.
675  $webfolderURI = $this->base_uri.$shortenedPath;
676  $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav").
677  substr($this->base_uri, strrpos($this->base_uri,':')).
678  $shortenedPath;
679  ;
680  $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav").
681  substr($this->base_uri, strrpos($this->base_uri,':')).
682  $shortenedPath
683  ;
684  $webfolderURI_IE = $this->base_uri.$shortenedPath;
685 
686  $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
687 
688  header('Content-Type: text/html; charset=UTF-8');
689  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
690  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
691  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
692  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
693  echo " <head>\n";
694  echo " <title>".sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle)."</title>\n";
695  echo " </head>\n";
696  echo " <body>\n";
697 
698  echo ilDAVServer::_getWebfolderInstructionsFor($webfolderTitle,
699  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
700  $this->clientOS,$this->clientOSFlavor);
701 
702  echo " </body>\n";
703  echo "</html>\n";
704 
705  // Logout anonymous user to force authentication after calling mount uri
706  if($ilUser->getId() == ANONYMOUS_USER_ID)
707  {
708  $GLOBALS['ilAuth']->logout();
709  session_destroy();
710  }
711 
712  exit;
713  }
723  private function getDir(&$objDAV, &$options)
724  {
725  global $ilias, $lng;
726 
727  // Activate tree cache
728  global $tree;
729  //$tree->useCache(true);
730 
731  $path = $this->davDeslashify($options['path']);
732 
733  // The URL of a directory must end with a slash.
734  // If it does not we are redirecting the browser.
735  // The slash is required, because we are using relative links in the
736  // HTML code we are generating below.
737  if ($path.'/' != $options['path'])
738  {
739  header('Location: '.$this->base_uri.$path.'/');
740  exit;
741  }
742 
743  header('Content-Type: text/html; charset=UTF-8');
744 
745  // fixed width directory column format
746  $format = "%15s %-19s %-s\n";
747 
748  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
749  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
750  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
751  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
752  echo "<head>\n";
753  echo "<title>".sprintf($lng->txt('webfolder_index_of'), $path)."</title>\n";
754 
755  // Create "anchorClick" behavior for for Internet Explorer
756  // This allows to create a link to a webfolder
757  echo "<style type=\"text/css\">\n";
758  echo "<!--\n";
759  echo "a {\n";
760  echo " behavior:url(#default#AnchorClick);\n";
761  echo "}\n";
762  echo "-->\n";
763  echo "</style>\n";
764 
765  echo "</head><body>\n";
766 
767  $hrefPath = '';
768  $pathComponents = explode('/',$path);
769  $uriComponents = array();
770  foreach ($pathComponents as $component)
771  {
772  $uriComponents[] = $this->davUrlEncode($component);
773  }
774  for ($i = 0; $i < count($pathComponents); $i++)
775  {
776  $displayName = htmlspecialchars($pathComponents[$i]);
777  if ($i != 0) {
778  $hrefPath .= '/';
779  }
780  $uriPath = implode('/', array_slice($uriComponents,0,$i + 1));
781  if ($i < 2)
782  {
783  // The first two path elements consist of the webdav.php script
784  // and the client id. These elements are not part of the
785  // directory structure and thus are not represented as links.
786  $hrefPath .= $displayName;
787  }
788  else
789  {
790  $hrefPath .= '<a href="'.$this->base_uri.$uriPath.'/">'.$displayName.'</a>';
791  }
792  }
793  echo "<h3>".sprintf($lng->txt('webfolder_index_of'), $hrefPath)."</h3>\n";
794 
795  // Display user id
796  if ($ilias->account->getLogin() == 'anonymous')
797  {
798  echo "<p><font size=\"-1\">".$lng->txt('not_logged_in')."</font><br>\n";
799  } else {
800  echo "<p><font size=\"-1\">".$lng->txt('login_as')." <i>"
801  .$ilias->account->getFirstname().' '
802  .$ilias->account->getLastname().' '
803  .' '.$ilias->account->getLogin().'</i> '
804  .', '.$lng->txt('client').' <i>'.$ilias->getClientId().'</i>.'
805  ."</font><p>\n";
806  }
807 
808  // Create "open as webfolder" link
809  $href = $this->base_uri.$uriPath;
810  // IE can not mount long paths. If the path has more than one element, we
811  // create a relative path with a ref-id.
812  if (count($pathComponents) > 2)
813  {
814  $hrefIE = $this->base_uri.'/'.CLIENT_ID.'/ref_'.$objDAV->getRefId();
815  } else {
816  $hrefIE = $href;
817  }
818  echo "<p><font size=\"-1\">".
819  sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions").
820  "</font></p>\n";
821  echo "<p><font size=\"-1\">".
822  sprintf($lng->txt('webfolder_mount_dir_with'),
823  "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
824  'webdav'.substr($href,4), // Konqueror
825  'dav'.substr($href,4), // Nautilus
826  $href.'?mount' // RFC 4709
827  )
828  ."</font></p>\n";
829 
830  echo "<pre>";
831  printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
832  echo "<hr>";
833 
834  $collectionCount = 0;
835  $fileCount = 0;
836  $children =& $objDAV->childrenWithPermission('visible,read');
837  foreach ($children as $childDAV) {
838  if ($childDAV->isCollection() && !$this->isFileHidden($childDAV))
839  {
840  $collectionCount++;
841  $name = $this->davUrlEncode($childDAV->getResourceName());
842  printf($format,
843  '-',
844  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
845  '<a href="'.$name.'/'.'">'.$childDAV->getDisplayName()."</a>");
846  }
847  }
848  foreach ($children as $childDAV) {
849  if ($childDAV->isFile() && !$this->isFileHidden($childDAV))
850  {
851  $fileCount++;
852  $name = $this->davUrlEncode($childDAV->getResourceName());
853  printf($format,
854  number_format($childDAV->getContentLength()),
855  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
856  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
857  }
858  }
859  foreach ($children as $childDAV) {
860  if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV))
861  {
862  $name = $this->davUrlEncode($childDAV->getResourceName());
863  printf($format,
864  'Lock',
865  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
866  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
867  }
868  }
869  echo "<hr>";
870  echo $collectionCount.' '.$lng->txt(($collectionCount == 1) ? 'folder' : 'folders').', ';
871  echo $fileCount.' '.$lng->txt(($fileCount == 1) ? 'file' : 'files').'.';
872  echo "</pre>";
873  echo "</body></html>\n";
874 
875  exit;
876  }
877 
878 
885  public function PUT(&$options)
886  {
887  global $ilUser;
888 
889  $this->writelog('PUT('.var_export($options, true).')');
890 
891  $path = $this->davDeslashify($options['path']);
892  $parent = dirname($path);
893  $name = $this->davBasename($path);
894 
895  // get dav object for path
896  $parentDAV =& $this->getObject($parent);
897 
898  // sanity check
899  if (is_null($parentDAV) || ! $parentDAV->isCollection()) {
900  return '409 Conflict';
901  }
902 
903  // Prevent putting of files which exceed upload limit
904  // FIXME: since this is an optional parameter, we should to do the
905  // same check again in function PUTfinished.
906  if ($options['content_length'] != null &&
907  $options['content_length'] > $this->getUploadMaxFilesize()) {
908 
909  $this->writelog('PUT is forbidden, because content length='.
910  $options['content_length'].' is larger than upload_max_filesize='.
911  $this->getUploadMaxFilesize().'in php.ini');
912 
913  return '403 Forbidden';
914  }
915 
916  // determine mime type
917  include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
918  $mime = ilMimeTypeUtil::getMimeType("", $name, $options['content_type']);
919 
920  $objDAV =& $this->getObject($path);
921  if (is_null($objDAV))
922  {
923  $ttype = $parentDAV->getILIASFileType();
924  $isperm = $parentDAV->isPermitted('create', $ttype);
925  if (! $isperm)
926  {
927  $this->writelog('PUT is forbidden, because user has no create permission');
928 
929  return '403 Forbidden';
930  }
931  $options["new"] = true;
932  $objDAV =& $parentDAV->createFile($name);
933  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
934  //$objDAV->setContentType($options['content_type']);
935  $objDAV->setContentType($mime);
936  if ($options['content_length'] != null)
937  {
938  $objDAV->setContentLength($options['content_length']);
939  }
940  $objDAV->write();
941  // Record write event
942  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
943  }
944  else if ($objDAV->isNullResource())
945  {
946  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
947  {
948  $this->writelog('PUT is forbidden, because user has no create permission');
949  return '403 Forbidden';
950  }
951  $options["new"] = false;
952  $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
953  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
954  //$objDAV->setContentType($options['content_type']);
955  $objDAV->setContentType($mime);
956  if ($options['content_length'] != null)
957  {
958  $objDAV->setContentLength($options['content_length']);
959  }
960  $objDAV->write();
961 
962  // Record write event
963  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
964  }
965  else
966  {
967  if (! $objDAV->isPermitted('write'))
968  {
969  $this->writelog('PUT is forbidden, because user has no write permission');
970  return '403 Forbidden';
971  }
972  $options["new"] = false;
973  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type'].' content_length='.$options['content_length']);
974 
975  // Create a new version if the previous version is not empty
976  if ($objDAV->getContentLength() != 0) {
977  $objDAV->createNewVersion();
978  }
979 
980  //$objDAV->setContentType($options['content_type']);
981  $objDAV->setContentType($mime);
982  if ($options['content_length'] != null)
983  {
984  $objDAV->setContentLength($options['content_length']);
985  }
986  $objDAV->write();
987 
988  // Record write event
989  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
990  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
991  }
992  // store this object, we reuse it in method PUTfinished
993  $this->putObjDAV = $objDAV;
994 
995  $out =& $objDAV->getContentOutputStream();
996  $this->writelog('PUT outputstream='.$out);
997 
998  return $out;
999  }
1000 
1007  public function PUTfinished(&$options)
1008  {
1009  $this->writelog('PUTfinished('.var_export($options, true).')');
1010 
1011  // Update the content length in the file object, if the
1012  // the client did not specify a content_length
1013  if ($options['content_length'] == null)
1014  {
1015  $objDAV = $this->putObjDAV;
1016  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1017  $objDAV->write();
1018  $this->putObjDAV = null;
1019  }
1020  return true;
1021  }
1022 
1023 
1030  public function MKCOL($options)
1031  {
1032  global $ilUser;
1033 
1034  $this->writelog('MKCOL('.var_export($options, true).')');
1035  $this->writelog('MKCOL '.$options['path']);
1036 
1037  $path =& $this->davDeslashify($options['path']);
1038  $parent =& dirname($path);
1039  $name =& $this->davBasename($path);
1040 
1041  // No body parsing yet
1042  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1043  return "415 Unsupported media type";
1044  }
1045 
1046  // Check if an object with the path already exists.
1047  $objDAV =& $this->getObject($path);
1048  if (! is_null($objDAV))
1049  {
1050  return '405 Method not allowed';
1051  }
1052 
1053  // get parent dav object for path
1054  $parentDAV =& $this->getObject($parent);
1055 
1056  // sanity check
1057  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1058  {
1059  return '409 Conflict';
1060  }
1061 
1062  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1063  {
1064  return '403 Forbidden';
1065  }
1066 
1067  // XXX Implement code that Handles null resource here
1068 
1069  $objDAV = $parentDAV->createCollection($name);
1070 
1071  if ($objDAV != null)
1072  {
1073  // Record write event
1074  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1075  }
1076 
1077  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1078  return $result;
1079  }
1080 
1081 
1088  public function DELETE($options)
1089  {
1090  global $ilUser;
1091 
1092  $this->writelog('DELETE('.var_export($options, true).')');
1093  $this->writelog('DELETE '.$options['path']);
1094 
1095  // get dav object for path
1096  $path =& $this->davDeslashify($options['path']);
1097  $parentDAV =& $this->getObject(dirname($path));
1098  $objDAV =& $this->getObject($path);
1099 
1100  // sanity check
1101  if (is_null($objDAV) || $objDAV->isNullResource())
1102  {
1103  return '404 Not Found';
1104  }
1105  if (! $objDAV->isPermitted('delete'))
1106  {
1107  return '403 Forbidden';
1108  }
1109 
1110  $parentDAV->remove($objDAV);
1111 
1112  // Record write event
1113  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1114 
1115  return '204 No Content';
1116  }
1117 
1124  public function MOVE($options)
1125  {
1126  global $ilUser;
1127 
1128  $this->writelog('MOVE('.var_export($options, true).')');
1129  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1130 
1131  // Get path names
1132  $src = $this->davDeslashify($options['path']);
1133  $srcParent = dirname($src);
1134  $srcName = $this->davBasename($src);
1135  $dst = $this->davDeslashify($options['dest']);
1136 
1137  $dstParent = dirname($dst);
1138  $dstName = $this->davBasename($dst);
1139  $this->writelog('move '.$dst.' dstname='.$dstName);
1140  // Source and destination must not be the same
1141  if ($src == $dst)
1142  {
1143  return '409 Conflict (source and destination are the same)';
1144  }
1145 
1146  // Destination must not be in a subtree of source
1147  if (substr($dst,strlen($src)+1) == $src.'/')
1148  {
1149  return '409 Conflict (destination is in subtree of source)';
1150  }
1151 
1152  // Get dav objects for path
1153  $srcDAV =& $this->getObject($src);
1154  $dstDAV =& $this->getObject($dst);
1155  $srcParentDAV =& $this->getObject($srcParent);
1156  $dstParentDAV =& $this->getObject($dstParent);
1157 
1158  // Source must exist
1159  if ($srcDAV == null)
1160  {
1161  return '409 Conflict (source does not exist)';
1162  }
1163 
1164  // Overwriting is only allowed, if overwrite option is set to 'T'
1165  $isOverwritten = false;
1166  if ($dstDAV != null)
1167  {
1168  if ($options['overwrite'] == 'T')
1169  {
1170  // Delete the overwritten destination
1171  if ($dstDAV->isPermitted('delete'))
1172  {
1173  $dstParentDAV->remove($dstDAV);
1174  $dstDAV = null;
1175  $isOverwritten = true;
1176  } else {
1177  return '403 Not Permitted';
1178  }
1179  } else {
1180  return '412 Precondition Failed';
1181  }
1182  }
1183 
1184  // Parents of destination must exist
1185  if ($dstParentDAV == null)
1186  {
1187  return '409 Conflict (parent of destination does not exist)';
1188  }
1189 
1190  if ($srcParent == $dstParent)
1191  {
1192  // Rename source, if source and dest are in same parent
1193 
1194  // Check permission
1195  if (! $srcDAV->isPermitted('write'))
1196  {
1197  return '403 Forbidden';
1198  }
1199  $this->writelog('rename dstName='.$dstName);
1200  $srcDAV->setResourceName($dstName);
1201  $srcDAV->write();
1202  } else {
1203  // Move source, if source and dest are in same parent
1204 
1205 
1206  if (! $srcDAV->isPermitted('delete'))
1207  {
1208  return '403 Forbidden';
1209  }
1210 
1211  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1212  {
1213  return '403 Forbidden';
1214  }
1215  $dstParentDAV->addMove($srcDAV, $dstName);
1216  }
1217 
1218  // Record write event
1219  if ($isOverwritten)
1220  {
1221  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1222  }
1223  else
1224  {
1225  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1226  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1227  }
1228 
1229  return ($isOverwritten) ? '204 No Content' : '201 Created';
1230  }
1231 
1238  public function COPY($options, $del=false)
1239  {
1240  global $ilUser;
1241  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1242  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1243 
1244  // no copying to different WebDAV Servers
1245  if (isset($options["dest_url"])) {
1246  return "502 bad gateway";
1247  }
1248 
1249  $src = $this->davDeslashify($options['path']);
1250  $srcParent = dirname($src);
1251  $srcName = $this->davBasename($src);
1252  $dst = $this->davDeslashify($options['dest']);
1253  $dstParent = dirname($dst);
1254  $dstName = $this->davBasename($dst);
1255 
1256  // sanity check
1257  if ($src == $dst)
1258  {
1259  return '409 Conflict'; // src and dst are the same
1260  }
1261 
1262  if (substr($dst,strlen($src)+1) == $src.'/')
1263  {
1264  return '409 Conflict'; // dst is in subtree of src
1265  }
1266 
1267  $this->writelog('COPY src='.$src.' dst='.$dst);
1268  // get dav object for path
1269  $srcDAV =& $this->getObject($src);
1270  $dstDAV =& $this->getObject($dst);
1271  $dstParentDAV =& $this->getObject($dstParent);
1272 
1273  if (is_null($srcDAV) || $srcDAV->isNullResource())
1274  {
1275  return '409 Conflict'; // src does not exist
1276  }
1277  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1278  {
1279  return '409 Conflict'; // parent of dst does not exist
1280  }
1281  $isOverwritten = false;
1282 
1283  // XXX Handle nulltype for dstDAV
1284  if (! is_null($dstDAV))
1285  {
1286  if ($options['overwrite'] == 'T')
1287  {
1288  if ($dstDAV->isPermitted('delete'))
1289  {
1290  $dstParentDAV->remove($dstDAV);
1291  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1292 
1293  $dstDAV = null;
1294  $isOverwritten = true;
1295  } else {
1296  return '403 Forbidden';
1297  }
1298  } else {
1299  return '412 Precondition Failed';
1300  }
1301  }
1302 
1303  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1304  {
1305  return '403 Forbidden';
1306  }
1307  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1308 
1309  // Record write event
1310  ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1311  $srcDAV->getObjectId(), $ilUser->getId());
1312  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1313 
1314  return ($isOverwritten) ? '204 No Content' : '201 Created';
1315  }
1316 
1323  public function PROPPATCH(&$options)
1324  {
1325  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1326  $this->writelog('PROPPATCH '.$options['path']);
1327 
1328  // get dav object for path
1329  $path =& $this->davDeslashify($options['path']);
1330  $objDAV =& $this->getObject($path);
1331 
1332  // sanity check
1333  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1334 
1335  $isPermitted = $objDAV->isPermitted('write');
1336  foreach($options['props'] as $key => $prop) {
1337  if (!$isPermitted || $prop['ns'] == 'DAV:')
1338  {
1339  $options['props'][$key]['status'] = '403 Forbidden';
1340  } else {
1341  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1342  }
1343  }
1344 
1345  return "";
1346  }
1347 
1348 
1355  public function LOCK(&$options)
1356  {
1357  global $ilias;
1358  $this->writelog('LOCK('.var_export($options, true).')');
1359  $this->writelog('LOCK '.$options['path']);
1360 
1361  // Check if an object with the path exists.
1362  $path =& $this->davDeslashify($options['path']);
1363  $objDAV =& $this->getObject($path);
1364  // Handle null-object locking
1365  // --------------------------
1366  if (is_null($objDAV))
1367  {
1368  $this->writelog('LOCK handling null-object locking...');
1369 
1370  // If the name does not exist, we create a null-object for it
1371  if (isset($options["update"]))
1372  {
1373  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1374  return '412 Precondition Failed';
1375  }
1376 
1377  $parent = dirname($path);
1378  $parentDAV =& $this->getObject($parent);
1379  if (is_null($parentDAV))
1380  {
1381  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1382  return '404 Not Found';
1383  }
1384  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1385  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1386  {
1387  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1388  return '403 Forbidden';
1389  }
1390 
1391  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1392  $this->writelog('created null resource for '.$path);
1393  }
1394 
1395  // ---------------------
1396  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1397  {
1398  $this->writelog('LOCK lock failed - user has no write permission.');
1399  return '403 Forbidden';
1400  }
1401 
1402  // XXX - Check if there are other locks on the resource
1403  if (!isset($options['timeout']) || is_array($options['timeout']))
1404  {
1405  $options["timeout"] = time()+360; // 6min.
1406  }
1407 
1408  if(isset($options["update"])) { // Lock Update
1409  $this->writelog('LOCK update token='.var_export($options,true));
1410  $success = $this->locks->updateLockWithoutCheckingDAV(
1411  $objDAV,
1412  $options['update'],
1413  $options['timeout']
1414  );
1415  if ($success)
1416  {
1417  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1418  if ($data['ilias_owner'] == $ilias->account->getId())
1419  {
1420  $owner = $data['dav_owner'];
1421  } else {
1422  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1423  }
1424  $options['owner'] = $owner;
1425  $options['locktoken'] = $data['token'];
1426  $options['timeout'] = $data['expires'];
1427  $options['depth'] = $data['depth'];
1428  $options['scope'] = $data['scope'];
1429  $options['type'] = $data['scope'];
1430  }
1431 
1432  } else {
1433  $this->writelog('LOCK create new lock');
1434 
1435  // XXX - Attempting to create a recursive exclusive lock
1436  // on a collection must fail, if any of nodes in the subtree
1437  // of the collection already has a lock.
1438  // XXX - Attempting to create a recursive shared lock
1439  // on a collection must fail, if any of nodes in the subtree
1440  // of the collection already has an exclusive lock.
1441  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1442  $this->writelog('lock owner='.$owner);
1443  $success = $this->locks->lockWithoutCheckingDAV(
1444  $objDAV,
1445  $ilias->account->getId(),
1446  trim($options['owner']),
1447  $options['locktoken'],
1448  $options['timeout'],
1449  $options['depth'],
1450  $options['scope']
1451  );
1452  }
1453 
1454  // Note: As a workaround for the Microsoft WebDAV Client, we return
1455  // true/false here (resulting in the status '200 OK') instead of
1456  // '204 No Content').
1457  //return ($success) ? '204 No Content' : false;
1458  return $success;
1459  }
1460 
1467  public function UNLOCK(&$options)
1468  {
1469  global $log, $ilias;
1470  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1471  $this->writelog('UNLOCK '.$options['path']);
1472 
1473  // Check if an object with the path exists.
1474  $path =& $this->davDeslashify($options['path']);
1475  $objDAV =& $this->getObject($path);
1476  if (is_null($objDAV)) {
1477  return '404 Not Found';
1478  }
1479  if (! $objDAV->isPermitted('write')) {
1480  return '403 Forbidden';
1481  }
1482 
1483  $success = $this->locks->unlockWithoutCheckingDAV(
1484  $objDAV,
1485  $options['token']
1486  );
1487 
1488  // Delete null resource object if there are no locks associated to
1489  // it anymore
1490  if ($objDAV->isNullResource()
1491  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1492  {
1493  $parent = dirname($this->davDeslashify($options['path']));
1494  $parentDAV =& $this->getObject($parent);
1495  $parentDAV->remove($objDAV);
1496  }
1497 
1498  // Workaround for Mac OS X: We must return 200 here instead of
1499  // 204.
1500  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1501  return ($success) ? '200 OK' : '412 Precondition Failed';
1502  }
1503 
1516  protected function checkLock($path)
1517  {
1518  global $ilias;
1519 
1520  $this->writelog('checkLock('.$path.')');
1521  $result = null;
1522 
1523  // get dav object for path
1524  //$objDAV = $this->getObject($path);
1525 
1526  // convert DAV path into ilObjectDAV path
1527  $objPath = $this->toObjectPath($path);
1528  if (! is_null($objPath))
1529  {
1530  $objDAV = $objPath[count($objPath) - 1];
1531  $locks = $this->locks->getLocksOnPathDAV($objPath);
1532  foreach ($locks as $lock)
1533  {
1534  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1535  && $lock['node_id'] == $objDAV->getNodeId();
1536 
1537  // Check all locks on last object in path,
1538  // but only locks with depth infinity on parent objects.
1539  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1540  {
1541  // DAV Clients expects to see their own owner name in
1542  // the locks. Since these names are not unique (they may
1543  // just be the name of the local user running the DAV client)
1544  // we return the ILIAS user name in all other cases.
1545  if ($lock['ilias_owner'] == $ilias->account->getId())
1546  {
1547  $owner = $lock['dav_owner'];
1548  } else {
1549  $owner = $this->getLogin($lock['ilias_owner']);
1550  }
1551 
1552  // FIXME - Shouldn't we collect all locks instead of
1553  // using an arbitrary one?
1554  $result = array(
1555  "type" => "write",
1556  "obj_id" => $lock['obj_id'],
1557  "node_id" => $lock['node_id'],
1558  "scope" => $lock['scope'],
1559  "depth" => $lock['depth'],
1560  "owner" => $owner,
1561  "token" => $lock['token'],
1562  "expires" => $lock['expires']
1563  );
1564  if ($lock['scope'] == 'exclusive')
1565  {
1566  // If there is an exclusive lock in the path, it
1567  // takes precedence over all non-exclusive locks in
1568  // parent nodes. Therefore we can can finish collecting
1569  // locks.
1570  break;
1571  }
1572  }
1573  }
1574  }
1575  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1576 
1577  return $result;
1578  }
1579 
1584  protected function getLogin($userId)
1585  {
1586  $login = ilObjUser::_lookupLogin($userId);
1587  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1588  return $login;
1589  }
1590 
1591 
1598  private function getObject($davPath)
1599  {
1600  global $tree;
1601 
1602 
1603  // If the second path elements starts with 'file_', the following
1604  // characters of the path element directly identify the ref_id of
1605  // a file object.
1606  $davPathComponents = split('/',substr($davPath,1));
1607  if (count($davPathComponents) > 1 &&
1608  substr($davPathComponents[1],0,5) == 'file_')
1609  {
1610  $ref_id = substr($davPathComponents[1],5);
1611  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1612 
1613  // Poor IE needs this, in order to successfully display
1614  // PDF documents
1615  header('Pragma: private');
1616  }
1617  else
1618  {
1619  $nodePath = $this->toNodePath($davPath);
1620  if ($nodePath == null && count($davPathComponents) == 1)
1621  {
1622  return ilObjectDAV::createObject(-1,'mountPoint');
1623  }
1624  }
1625  if (is_null($nodePath))
1626  {
1627  return null;
1628  } else {
1629  $top = $nodePath[count($nodePath) - 1];
1630  return ilObjectDAV::createObject($top['child'],$top['type']);
1631  }
1632  }
1639  private function toObjectPath($davPath)
1640  {
1641  $this->writelog('toObjectPath('.$davPath);
1642  global $tree;
1643 
1644  $nodePath = $this->toNodePath($davPath);
1645 
1646  if (is_null($nodePath))
1647  {
1648  return null;
1649  } else {
1650  $objectPath = array();
1651  foreach ($nodePath as $node)
1652  {
1653  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1654  if (is_null($pathElement))
1655  {
1656  break;
1657  }
1658  $objectPath[] = $pathElement;
1659  }
1660  return $objectPath;
1661  }
1662  }
1663 
1675  public function toNodePath($davPath)
1676  {
1677  global $tree;
1678  $this->writelog('toNodePath('.$davPath.')...');
1679 
1680  // Split the davPath into path titles
1681  $titlePath = split('/',substr($davPath,1));
1682 
1683  // Remove the client id from the beginning of the title path
1684  if (count($titlePath) > 0)
1685  {
1686  array_shift($titlePath);
1687  }
1688 
1689  // If the last path title is empty, remove it
1690  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1691  {
1692  array_pop($titlePath);
1693  }
1694 
1695  // If the path is empty, return null
1696  if (count($titlePath) == 0)
1697  {
1698  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1699  return null;
1700  }
1701 
1702  // If the path is an absolute path, ref_id is null.
1703  $ref_id = null;
1704 
1705  // If the path is a relative folder path, convert it into an absolute path
1706  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1707  {
1708  $ref_id = substr($titlePath[0],4);
1709  array_shift($titlePath);
1710  }
1711 
1712  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1713 
1714  $this->writelog('toNodePath():'.var_export($nodePath,true));
1715  return $nodePath;
1716  }
1717 
1724  private function davDeslashify($path)
1725  {
1727 
1728  if ($path[strlen($path)-1] == '/') {
1729  $path = substr($path,0, strlen($path) - 1);
1730  }
1731  $this->writelog('davDeslashify:'.$path);
1732  return $path;
1733  }
1734 
1741  private function davBasename($path)
1742  {
1743  $components = split('/',$path);
1744  return count($components) == 0 ? '' : $components[count($components) - 1];
1745  }
1746 
1753  protected function writelog($message)
1754  {
1755  // Only write log message, if we are in debug mode
1756  if ($this->isDebug)
1757  {
1758  global $ilLog, $ilias;
1759  if ($ilLog)
1760  {
1761  if ($message == '---')
1762  {
1763  $ilLog->write('');
1764  } else {
1765  $ilLog->write(
1766  $ilias->account->getLogin()
1767  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1768  .' ilDAVServer.'.str_replace("\n",";",$message)
1769  );
1770  }
1771  }
1772  else
1773  {
1774  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1775  fwrite($fh, date('Y-m-d H:i:s '));
1776  fwrite($fh, str_replace("\n",";",$message));
1777  fwrite($fh, "\n\n");
1778  fclose($fh);
1779  }
1780  }
1781  }
1782 
1795  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1796  {
1797  if ($genericURI) {
1798  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1799  $query = null;
1800  } else if ($this->clientOS == 'windows') {
1801  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1802  $query = 'mount-instructions';
1803  } else if ($this->clientBrowser == 'konqueror') {
1804  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1805  $query = null;
1806  } else if ($this->clientBrowser == 'nautilus') {
1807  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1808  $query = null;
1809  } else {
1810  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1811  $query = 'mount-instructions';
1812  }
1813  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1814  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1815 
1816  $uri = $baseUri.'/ref_'.$refId.'/';
1817  if ($query != null)
1818  {
1819  $uri .= '?'.$query;
1820  }
1821 
1822  return $uri;
1823  }
1838  function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1839  {
1840  if ($this->clientOS == 'windows') {
1841  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1842  $query = null;
1843  } else if ($this->clientBrowser == 'konqueror') {
1844  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1845  $query = null;
1846  } else if ($this->clientBrowser == 'nautilus') {
1847  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1848  $query = null;
1849  } else {
1850  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1851  $query = null;
1852  }
1853  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1854  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1855 
1856  $uri = $baseUri.'/ref_'.$refId.'/';
1857  if ($query != null)
1858  {
1859  $uri .= '?'.$query;
1860  }
1861 
1862  return $uri;
1863  }
1875  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1876  {
1877  $nodeId = 0;
1878  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1879  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1880  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1881 
1882  if (! is_null($ressourceName) && ! is_null($parentRefId))
1883  {
1884  // Quickly create URI from the known data without needing SQL queries
1885  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1886  } else {
1887  // Create URI and use some SQL queries to get the missing data
1888  global $tree;
1889  $nodePath = $tree->getNodePath($refId);
1890 
1891  if (is_null($nodePath) || count($nodePath) < 2)
1892  {
1893  // No object path? Return null - file is not in repository.
1894  $uri = null;
1895  } else {
1896  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1897  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1898  }
1899  }
1900  return $uri;
1901  }
1902 
1920  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1921  {
1922  $nodeId = 0;
1923  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1924  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1925  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1926 
1927  if (! is_null($ressourceName) && ! is_null($parentRefId))
1928  {
1929  // Quickly create URI from the known data without needing SQL queries
1930  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1931  } else {
1932  // Create URI and use some SQL queries to get the missing data
1933  global $tree;
1934  $nodePath = $tree->getNodePath($refId);
1935 
1936  if (is_null($nodePath) || count($nodePath) < 2)
1937  {
1938  // No object path? Return null - file is not in repository.
1939  $uri = null;
1940  } else {
1941  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1942  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1943  }
1944  }
1945  return $uri;
1946  }
1947 
1953  public function isWebDAVoverHTTPS() {
1954  if ($this->isHTTPS == null) {
1955  global $ilSetting;
1956  require_once './Services/Http/classes/class.ilHTTPS.php';
1957  $https = new ilHTTPS();
1958  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1959  }
1960  return $this->isHTTPS;
1961  }
1962 
1971  public static function _isActive()
1972  {
1973  global $ilClientIniFile;
1974  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1975  @include_once("Auth/HTTP.php");
1976  }
1982  public static function _isActionsVisible()
1983  {
1984  global $ilClientIniFile;
1985  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
1986  }
1987 
1997  public static function _getDefaultWebfolderInstructions()
1998  {
1999  global $lng;
2000  return $lng->txt('webfolder_instructions_text');
2001  }
2002 
2029  public static function _getWebfolderInstructionsFor($webfolderTitle,
2030  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2031  $os = 'unknown', $osFlavor = 'unknown')
2032  {
2033  global $ilSetting;
2034 
2035  $settings = new ilSetting('file_access');
2036  $str = $settings->get('custom_webfolder_instructions', '');
2037  if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2038  {
2040  }
2041  if(is_file('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm')){
2042  $str = fread(fopen('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm', "rb"),filesize('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm'));
2043  }
2044  $str=utf8_encode($str);
2045 
2046  preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2047  $refID=$matches[0][0];
2048 
2049  $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2050  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2051  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2052  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2053  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2054  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2055  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2056 
2057  if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false){
2058  $str = preg_replace('/\[IF_IEXPLORE\]((?:.|\n)*)\[\/IF_IEXPLORE\]/','\1', $str);
2059  }else{
2060  $str = preg_replace('/\[IF_NOTIEXPLORE\]((?:.|\n)*)\[\/IF_NOTIEXPLORE\]/','\1', $str);
2061  }
2062 
2063  switch ($os)
2064  {
2065  case 'windows' :
2066  $operatingSystem = 'WINDOWS';
2067  break;
2068  case 'unix' :
2069  switch ($osFlavor)
2070  {
2071  case 'osx' :
2072  $operatingSystem = 'MAC';
2073  break;
2074  case 'linux' :
2075  $operatingSystem = 'LINUX';
2076  break;
2077  default :
2078  $operatingSystem = 'LINUX';
2079  break;
2080  }
2081  break;
2082  default :
2083  $operatingSystem = 'UNKNOWN';
2084  break;
2085  }
2086 
2087  if ($operatingSystem != 'UNKNOWN')
2088  {
2089  $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2090  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2091  }
2092  else
2093  {
2094  $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2095  }
2096  return $str;
2097  }
2098 
2104  private function getUploadMaxFilesize() {
2105  $val = ini_get('upload_max_filesize');
2106 
2107  $val = trim($val);
2108  $last = strtolower($val[strlen($val)-1]);
2109  switch($last) {
2110  // The 'G' modifier is available since PHP 5.1.0
2111  case 'g':
2112  $val *= 1024;
2113  case 'm':
2114  $val *= 1024;
2115  case 'k':
2116  $val *= 1024;
2117  }
2118 
2119  return $val;
2120  }
2121 }
2122 // END WebDAV
2123 ?>