ILIAS  eassessment Revision 61809
 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  $this->clientOSFlavor = 'xp';
152 
153  } else if (strpos($userAgent,'darwin') !== false
154  || strpos($userAgent,'macintosh') !== false
155  || strpos($userAgent,'linux') !== false
156  || strpos($userAgent,'solaris') !== false
157  || strpos($userAgent,'aix') !== false
158  || strpos($userAgent,'unix') !== false
159  || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
160  )
161  {
162  $this->clientOS = 'unix';
163  if (strpos($userAgent,'linux') !== false)
164  {
165  $this->clientOSFlavor = 'linux';
166  }
167  else if (strpos($userAgent,'macintosh') !== false)
168  {
169  $this->clientOSFlavor = 'osx';
170  }
171  }
172  if (strpos($userAgent,'konqueror') !== false)
173  {
174  $this->clientBrowser = 'konqueror';
175  }
176  }
177 
182  public static function getInstance()
183  {
184  if(self::$instance != NULL)
185  {
186  return self::$instance;
187  }
188  return self::$instance = new ilDAVServer();
189  }
190 
194  public function serveRequest()
195  {
196  // die quickly if plugin is deactivated
197  if (!self::_isActive())
198  {
199  $this->writelog(__METHOD__.' WebDAV disabled. Aborting');
200  $this->http_status('403 Forbidden');
201  echo '<html><body><h1>Sorry</h1>'.
202  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
203  '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
204  '</body></html>';
205  return;
206  }
207 
208  try {
209  $start = time();
210  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
212  $end = time();
213  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
214  $this->writelog('---');
215  }
216  catch (Exception $e)
217  {
218  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
219  }
220  }
221 
256  private function davUrlEncode($path)
257  {
258  // We compose the path to Unicode Normal Form NFC
259  // This ensures that diaeresis and other special characters
260  // are treated uniformly on Windows and on Mac OS X
262 
263  $c = explode('/',$path);
264  for ($i = 0; $i < count($c); $i++)
265  {
266  $c[$i] = str_replace('+','%20',urlencode($c[$i]));
267  }
268  return implode('/',$c);
269  }
270 
278  public function PROPFIND(&$options, &$files)
279  {
280  // Activate tree cache
281  global $tree;
282  //$tree->useCache(true);
283 
284  $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
285  $this->writelog('PROPFIND '.$options['path']);
286 
287  // get dav object for path
288  $path =& $this->davDeslashify($options['path']);
289  $objDAV =& $this->getObject($path);
290 
291  // prepare property array
292  $files['files'] = array();
293 
294  // sanity check
295  if (is_null($objDAV)) {
296  return false;
297  }
298  if (! $objDAV->isPermitted('visible,read')) {
299  return '403 Forbidden';
300  }
301 
302  // store information for the requested path itself
303  // FIXME : create display name for object.
304  $encodedPath = $this->davUrlEncode($path);
305 
306  $GLOBALS['ilLog']->write(print_r($encodedPath,true));
307 
308  $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
309 
310  // information for contained resources requested?
311  if (!empty($options['depth'])) {
312  // The breadthFirst list holds the collections which we have not
313  // processed yet. If depth is infinity we append unprocessed collections
314  // to the end of this list, and remove processed collections from
315  // the beginning of this list.
316  $breadthFirst = array($objDAV);
317  $objDAV->encodedPath = $encodedPath;
318 
319  while (count($breadthFirst) > 0) {
320  // remove a collection from the beginning of the breadthFirst list
321  $collectionDAV = array_shift($breadthFirst);
322  $childrenDAV =& $collectionDAV->childrenWithPermission('visible');
323  foreach ($childrenDAV as $childDAV)
324  {
325  // On duplicate names, work with the older object (the one with the
326  // smaller object id).
327  foreach ($childrenDAV as $duplChildDAV)
328  {
329  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
330  $duplChildDAV->getResourceName() == $childDAV->getResourceName())
331  {
332  continue 2;
333  }
334  }
335 
336  // only add visible objects to the file list
337  if (!$this->isFileHidden($childDAV))
338  {
339  $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
340  $files['files'][] =& $this->fileinfo(
341  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
342  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
343  $childDAV
344  );
345  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
346  // add a collection to the end of the breadthFirst list
347  $breadthFirst[] = $childDAV;
348  $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
349  }
350  }
351  }
352  }
353  }
354 
355  // Record read event but don't catch up with write events, because
356  // with WebDAV, a user can not see all objects contained in a folder.
358  {
359  global $ilUser;
360  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
361  $objDAV->getObjectId(), $ilUser->getId(), false);
362  }
363  // ok, all done
364  $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
365  return true;
366  }
367 
378  private function isFileHidden(&$objDAV)
379  {
380  // Hide null resources which haven't got an active lock
381  if ($objDAV->isNullResource()) {
382  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
383  return;
384  }
385  }
386 
387  $name = $objDAV->getResourceName();
388  $isFileHidden = false;
389  switch ($this->clientOS)
390  {
391  case 'unix' :
392  // Hide Windows thumbnail files, and files which start with '~$'.
393  $isFileHidden =
394  $name == 'Thumbs.db'
395  || substr($name, 0, 2) == '~$';
396  // Hide files which contain /
397  $isFileHidden |= preg_match('/\\//', $name);
398  break;
399  case 'windows' :
400  // Hide files that start with '.'.
401  $isFileHidden = substr($name, 0, 1) == '.';
402  // Hide files which contain \ / : * ? " < > |
403  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
404  break;
405  default :
406  // Hide files which contain /
407  $isFileHidden |= preg_match('/\\//', $name);
408  break;
409  }
410  $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
411  return $isFileHidden;
412  }
413 
421  private function fileinfo($resourcePath, $displayPath, &$objDAV)
422  {
423  global $ilias;
424 
425  $this->writelog('fileinfo('.$resourcePath.')');
426  // create result array
427  $info = array();
428  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
429  * end of a resource path to a collection.
430  * However Mac OS X does not like this!
431  */
432  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
433  $info['path'] = $resourcePath.'/';
434  } else {
435  $info['path'] = $resourcePath;
436  }
437 
438  $info['props'] = array();
439 
440  // no special beautified displayname here ...
441  $info["props"][] =& $this->mkprop("displayname", $displayPath);
442 
443  // creation and modification time
444  $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
445  $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
446 
447  // directory (WebDAV collection)
448  $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
449  $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
450  $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
451 
452  // Only show supported locks for users who have write permission
453  if ($objDAV->isPermitted('write'))
454  {
455  $info["props"][] =& $this->mkprop("supportedlock",
456  '<D:lockentry>'
457  .'<D:lockscope><D:exclusive/></D:lockscope>'
458  .'<D:locktype><D:write/></D:locktype>'
459  .'</D:lockentry>'
460  .'<D:lockentry>'
461  .'<D:lockscope><D:shared/></D:lockscope>'
462  .'<D:locktype><D:write/></D:locktype>'
463  .'</D:lockentry>'
464  );
465  }
466 
467  // Maybe we should only show locks on objects for users who have write permission.
468  // But if we don't show these locks, users who have write permission in an object
469  // further down in a hierarchy can't see who is locking their object.
470  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
471  $lockdiscovery = '';
472  foreach ($locks as $lock)
473  {
474  // DAV Clients expects to see their own owner name in
475  // the locks. Since these names are not unique (they may
476  // just be the name of the local user running the DAV client)
477  // we return the ILIAS user name in all other cases.
478  if ($lock['ilias_owner'] == $ilias->account->getId())
479  {
480  $owner = $lock['dav_owner'];
481  } else {
482  $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
483  }
484  $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
485 
486  $lockdiscovery .=
487  '<D:activelock>'
488  .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
489  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
490  .'<D:locktype><D:write/></D:locktype>'
491  .'<D:depth>'.$lock['depth'].'</D:depth>'
492  .'<D:owner>'.$owner.'</D:owner>'
493 
494  // more than a million is considered an absolute timestamp
495  // less is more likely a relative value
496  .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
497  .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
498  .'</D:activelock>'
499  ;
500  }
501  if (strlen($lockdiscovery) > 0)
502  {
503  $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
504  }
505 
506  // get additional properties from database
507  $properties = $this->properties->getAll($objDAV);
508  foreach ($properties as $prop)
509  {
510  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
511  }
512 
513  //$this->writelog('fileinfo():'.var_export($info, true));
514  return $info;
515  }
516 
528  public function GET(&$options)
529  {
530  global $ilUser;
531 
532  $this->writelog('GET('.var_export($options, true).')');
533  $this->writelog('GET('.$options['path'].')');
534 
535  // get dav object for path
536  $path = $this->davDeslashify($options['path']);
537  $objDAV =& $this->getObject($path);
538 
539  // sanity check
540  if (is_null($objDAV) || $objDAV->isNullResource())
541  {
542  return false;
543  }
544 
545  if (! $objDAV->isPermitted('visible,read'))
546  {
547  return '403 Forbidden';
548  }
549 
550  // is this a collection?
551  if ($objDAV->isCollection())
552  {
553  if (isset($_GET['mount']))
554  {
555  return $this->mountDir($objDAV, $options);
556  }
557  else if (isset($_GET['mount-instructions']))
558  {
559  return $this->showMountInstructions($objDAV, $options);
560  }
561  else
562  {
563  return $this->getDir($objDAV, $options);
564  }
565  }
566  // detect content type
567  $options['mimetype'] =& $objDAV->getContentType();
568  // detect modification time
569  // see rfc2518, section 13.7
570  // some clients seem to treat this as a reverse rule
571  // requiring a Last-Modified header if the getlastmodified header was set
572  $options['mtime'] =& $objDAV->getModificationTimestamp();
573 
574  // detect content length
575  $options['size'] =& $objDAV->getContentLength();
576 
577  // get content as stream or as data array
578  $options['stream'] =& $objDAV->getContentStream();
579  if (is_null($options['stream']))
580  {
581  $options['data'] =& $objDAV->getContentData();
582  }
583 
584  // Record read event and catch up write events
586  {
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');
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
943  {
944  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
945  }
946  }
947  else if ($objDAV->isNullResource())
948  {
949  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
950  {
951  $this->writelog('PUT is forbidden, because user has no create permission');
952  return '403 Forbidden';
953  }
954  $options["new"] = false;
955  $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
956  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
957  //$objDAV->setContentType($options['content_type']);
958  $objDAV->setContentType($mime);
959  if ($options['content_length'] != null)
960  {
961  $objDAV->setContentLength($options['content_length']);
962  }
963  $objDAV->write();
964 
965  // Record write event
967  {
968  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
969  }
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
996  {
997  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
998  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
999  }
1000  }
1001  // store this object, we reuse it in method PUTfinished
1002  $this->putObjDAV = $objDAV;
1003 
1004  $out =& $objDAV->getContentOutputStream();
1005  $this->writelog('PUT outputstream='.$out);
1006 
1007  return $out;
1008  }
1009 
1016  public function PUTfinished(&$options)
1017  {
1018  $this->writelog('PUTfinished('.var_export($options, true).')');
1019 
1020  // Update the content length in the file object, if the
1021  // the client did not specify a content_length
1022  if ($options['content_length'] == null)
1023  {
1024  $objDAV = $this->putObjDAV;
1025  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1026  $objDAV->write();
1027  $this->putObjDAV = null;
1028  }
1029  return true;
1030  }
1031 
1032 
1039  public function MKCOL($options)
1040  {
1041  global $ilUser;
1042 
1043  $this->writelog('MKCOL('.var_export($options, true).')');
1044  $this->writelog('MKCOL '.$options['path']);
1045 
1046  $path =& $this->davDeslashify($options['path']);
1047  $parent =& dirname($path);
1048  $name =& $this->davBasename($path);
1049 
1050  // No body parsing yet
1051  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1052  return "415 Unsupported media type";
1053  }
1054 
1055  // Check if an object with the path already exists.
1056  $objDAV =& $this->getObject($path);
1057  if (! is_null($objDAV))
1058  {
1059  return '405 Method not allowed';
1060  }
1061 
1062  // get parent dav object for path
1063  $parentDAV =& $this->getObject($parent);
1064 
1065  // sanity check
1066  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1067  {
1068  return '409 Conflict';
1069  }
1070 
1071  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1072  {
1073  return '403 Forbidden';
1074  }
1075 
1076  // XXX Implement code that Handles null resource here
1077 
1078  $objDAV = $parentDAV->createCollection($name);
1079 
1080  if ($objDAV != null)
1081  {
1082  // Record write event
1084  {
1085  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1086  }
1087  }
1088 
1089  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1090  return $result;
1091  }
1092 
1093 
1100  public function DELETE($options)
1101  {
1102  global $ilUser;
1103 
1104  $this->writelog('DELETE('.var_export($options, true).')');
1105  $this->writelog('DELETE '.$options['path']);
1106 
1107  // get dav object for path
1108  $path =& $this->davDeslashify($options['path']);
1109  $parentDAV =& $this->getObject(dirname($path));
1110  $objDAV =& $this->getObject($path);
1111 
1112  // sanity check
1113  if (is_null($objDAV) || $objDAV->isNullResource())
1114  {
1115  return '404 Not Found';
1116  }
1117  if (! $objDAV->isPermitted('delete'))
1118  {
1119  return '403 Forbidden';
1120  }
1121 
1122  $parentDAV->remove($objDAV);
1123 
1124  // Record write event
1126  {
1127  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1128  }
1129 
1130  return '204 No Content';
1131  }
1132 
1139  public function MOVE($options)
1140  {
1141  global $ilUser;
1142 
1143  $this->writelog('MOVE('.var_export($options, true).')');
1144  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1145 
1146  // Get path names
1147  $src = $this->davDeslashify($options['path']);
1148  $srcParent = dirname($src);
1149  $srcName = $this->davBasename($src);
1150  $dst = $this->davDeslashify($options['dest']);
1151 
1152  $dstParent = dirname($dst);
1153  $dstName = $this->davBasename($dst);
1154  $this->writelog('move '.$dst.' dstname='.$dstName);
1155  // Source and destination must not be the same
1156  if ($src == $dst)
1157  {
1158  return '409 Conflict (source and destination are the same)';
1159  }
1160 
1161  // Destination must not be in a subtree of source
1162  if (substr($dst,strlen($src)+1) == $src.'/')
1163  {
1164  return '409 Conflict (destination is in subtree of source)';
1165  }
1166 
1167  // Get dav objects for path
1168  $srcDAV =& $this->getObject($src);
1169  $dstDAV =& $this->getObject($dst);
1170  $srcParentDAV =& $this->getObject($srcParent);
1171  $dstParentDAV =& $this->getObject($dstParent);
1172 
1173  // Source must exist
1174  if ($srcDAV == null)
1175  {
1176  return '409 Conflict (source does not exist)';
1177  }
1178 
1179  // Overwriting is only allowed, if overwrite option is set to 'T'
1180  $isOverwritten = false;
1181  if ($dstDAV != null)
1182  {
1183  if ($options['overwrite'] == 'T')
1184  {
1185  // Delete the overwritten destination
1186  if ($dstDAV->isPermitted('delete'))
1187  {
1188  $dstParentDAV->remove($dstDAV);
1189  $dstDAV = null;
1190  $isOverwritten = true;
1191  } else {
1192  return '403 Not Permitted';
1193  }
1194  } else {
1195  return '412 Precondition Failed';
1196  }
1197  }
1198 
1199  // Parents of destination must exist
1200  if ($dstParentDAV == null)
1201  {
1202  return '409 Conflict (parent of destination does not exist)';
1203  }
1204 
1205  if ($srcParent == $dstParent)
1206  {
1207  // Rename source, if source and dest are in same parent
1208 
1209  // Check permission
1210  if (! $srcDAV->isPermitted('write'))
1211  {
1212  return '403 Forbidden';
1213  }
1214  $this->writelog('rename dstName='.$dstName);
1215  $srcDAV->setResourceName($dstName);
1216  $srcDAV->write();
1217  } else {
1218  // Move source, if source and dest are in same parent
1219 
1220 
1221  if (! $srcDAV->isPermitted('delete'))
1222  {
1223  return '403 Forbidden';
1224  }
1225 
1226  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1227  {
1228  return '403 Forbidden';
1229  }
1230  $dstParentDAV->addMove($srcDAV, $dstName);
1231  }
1232 
1233  // Record write event
1235  {
1236  if ($isOverwritten)
1237  {
1238  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1239  }
1240  else
1241  {
1242  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1243  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1244  }
1245  }
1246 
1247  return ($isOverwritten) ? '204 No Content' : '201 Created';
1248  }
1249 
1256  public function COPY($options, $del=false)
1257  {
1258  global $ilUser;
1259  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1260  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1261 
1262  // no copying to different WebDAV Servers
1263  if (isset($options["dest_url"])) {
1264  return "502 bad gateway";
1265  }
1266 
1267  $src = $this->davDeslashify($options['path']);
1268  $srcParent = dirname($src);
1269  $srcName = $this->davBasename($src);
1270  $dst = $this->davDeslashify($options['dest']);
1271  $dstParent = dirname($dst);
1272  $dstName = $this->davBasename($dst);
1273 
1274  // sanity check
1275  if ($src == $dst)
1276  {
1277  return '409 Conflict'; // src and dst are the same
1278  }
1279 
1280  if (substr($dst,strlen($src)+1) == $src.'/')
1281  {
1282  return '409 Conflict'; // dst is in subtree of src
1283  }
1284 
1285  $this->writelog('COPY src='.$src.' dst='.$dst);
1286  // get dav object for path
1287  $srcDAV =& $this->getObject($src);
1288  $dstDAV =& $this->getObject($dst);
1289  $dstParentDAV =& $this->getObject($dstParent);
1290 
1291  if (is_null($srcDAV) || $srcDAV->isNullResource())
1292  {
1293  return '409 Conflict'; // src does not exist
1294  }
1295  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1296  {
1297  return '409 Conflict'; // parent of dst does not exist
1298  }
1299  $isOverwritten = false;
1300 
1301  // XXX Handle nulltype for dstDAV
1302  if (! is_null($dstDAV))
1303  {
1304  if ($options['overwrite'] == 'T')
1305  {
1306  if ($dstDAV->isPermitted('delete'))
1307  {
1308  $dstParentDAV->remove($dstDAV);
1310  {
1311  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1312  }
1313 
1314  $dstDAV = null;
1315  $isOverwritten = true;
1316  } else {
1317  return '403 Forbidden';
1318  }
1319  } else {
1320  return '412 Precondition Failed';
1321  }
1322  }
1323 
1324  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1325  {
1326  return '403 Forbidden';
1327  }
1328  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1329 
1330  // Record write event
1332  {
1333  ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1334  $srcDAV->getObjectId(), $ilUser->getId());
1335  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1336  }
1337 
1338  return ($isOverwritten) ? '204 No Content' : '201 Created';
1339  }
1340 
1347  public function PROPPATCH(&$options)
1348  {
1349  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1350  $this->writelog('PROPPATCH '.$options['path']);
1351 
1352  // get dav object for path
1353  $path =& $this->davDeslashify($options['path']);
1354  $objDAV =& $this->getObject($path);
1355 
1356  // sanity check
1357  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1358 
1359  $isPermitted = $objDAV->isPermitted('write');
1360  foreach($options['props'] as $key => $prop) {
1361  if (!$isPermitted || $prop['ns'] == 'DAV:')
1362  {
1363  $options['props'][$key]['status'] = '403 Forbidden';
1364  } else {
1365  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1366  }
1367  }
1368 
1369  return "";
1370  }
1371 
1372 
1379  public function LOCK(&$options)
1380  {
1381  global $ilias;
1382  $this->writelog('LOCK('.var_export($options, true).')');
1383  $this->writelog('LOCK '.$options['path']);
1384 
1385  // Check if an object with the path exists.
1386  $path =& $this->davDeslashify($options['path']);
1387  $objDAV =& $this->getObject($path);
1388  // Handle null-object locking
1389  // --------------------------
1390  if (is_null($objDAV))
1391  {
1392  $this->writelog('LOCK handling null-object locking...');
1393 
1394  // If the name does not exist, we create a null-object for it
1395  if (isset($options["update"]))
1396  {
1397  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1398  return '412 Precondition Failed';
1399  }
1400 
1401  $parent = dirname($path);
1402  $parentDAV =& $this->getObject($parent);
1403  if (is_null($parentDAV))
1404  {
1405  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1406  return '404 Not Found';
1407  }
1408  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1409  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1410  {
1411  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1412  return '403 Forbidden';
1413  }
1414 
1415  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1416  $this->writelog('created null resource for '.$path);
1417  }
1418 
1419  // ---------------------
1420  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1421  {
1422  $this->writelog('LOCK lock failed - user has no write permission.');
1423  return '403 Forbidden';
1424  }
1425 
1426  // XXX - Check if there are other locks on the resource
1427  if (!isset($options['timeout']) || is_array($options['timeout']))
1428  {
1429  $options["timeout"] = time()+360; // 6min.
1430  }
1431 
1432  if(isset($options["update"])) { // Lock Update
1433  $this->writelog('LOCK update token='.var_export($options,true));
1434  $success = $this->locks->updateLockWithoutCheckingDAV(
1435  $objDAV,
1436  $options['update'],
1437  $options['timeout']
1438  );
1439  if ($success)
1440  {
1441  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1442  if ($data['ilias_owner'] == $ilias->account->getId())
1443  {
1444  $owner = $data['dav_owner'];
1445  } else {
1446  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1447  }
1448  $options['owner'] = $owner;
1449  $options['locktoken'] = $data['token'];
1450  $options['timeout'] = $data['expires'];
1451  $options['depth'] = $data['depth'];
1452  $options['scope'] = $data['scope'];
1453  $options['type'] = $data['scope'];
1454  }
1455 
1456  } else {
1457  $this->writelog('LOCK create new lock');
1458 
1459  // XXX - Attempting to create a recursive exclusive lock
1460  // on a collection must fail, if any of nodes in the subtree
1461  // of the collection already has a lock.
1462  // XXX - Attempting to create a recursive shared lock
1463  // on a collection must fail, if any of nodes in the subtree
1464  // of the collection already has an exclusive lock.
1465  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1466  $this->writelog('lock owner='.$owner);
1467  $success = $this->locks->lockWithoutCheckingDAV(
1468  $objDAV,
1469  $ilias->account->getId(),
1470  trim($options['owner']),
1471  $options['locktoken'],
1472  $options['timeout'],
1473  $options['depth'],
1474  $options['scope']
1475  );
1476  }
1477 
1478  // Note: As a workaround for the Microsoft WebDAV Client, we return
1479  // true/false here (resulting in the status '200 OK') instead of
1480  // '204 No Content').
1481  //return ($success) ? '204 No Content' : false;
1482  return $success;
1483  }
1484 
1491  public function UNLOCK(&$options)
1492  {
1493  global $log, $ilias;
1494  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1495  $this->writelog('UNLOCK '.$options['path']);
1496 
1497  // Check if an object with the path exists.
1498  $path =& $this->davDeslashify($options['path']);
1499  $objDAV =& $this->getObject($path);
1500  if (is_null($objDAV)) {
1501  return '404 Not Found';
1502  }
1503  if (! $objDAV->isPermitted('write')) {
1504  return '403 Forbidden';
1505  }
1506 
1507  $success = $this->locks->unlockWithoutCheckingDAV(
1508  $objDAV,
1509  $options['token']
1510  );
1511 
1512  // Delete null resource object if there are no locks associated to
1513  // it anymore
1514  if ($objDAV->isNullResource()
1515  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1516  {
1517  $parent = dirname($this->davDeslashify($options['path']));
1518  $parentDAV =& $this->getObject($parent);
1519  $parentDAV->remove($objDAV);
1520  }
1521 
1522  // Workaround for Mac OS X: We must return 200 here instead of
1523  // 204.
1524  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1525  return ($success) ? '200 OK' : '412 Precondition Failed';
1526  }
1527 
1540  protected function checkLock($path)
1541  {
1542  global $ilias;
1543 
1544  $this->writelog('checkLock('.$path.')');
1545  $result = null;
1546 
1547  // get dav object for path
1548  //$objDAV = $this->getObject($path);
1549 
1550  // convert DAV path into ilObjectDAV path
1551  $objPath = $this->toObjectPath($path);
1552  if (! is_null($objPath))
1553  {
1554  $objDAV = $objPath[count($objPath) - 1];
1555  $locks = $this->locks->getLocksOnPathDAV($objPath);
1556  foreach ($locks as $lock)
1557  {
1558  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1559  && $lock['node_id'] == $objDAV->getNodeId();
1560 
1561  // Check all locks on last object in path,
1562  // but only locks with depth infinity on parent objects.
1563  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1564  {
1565  // DAV Clients expects to see their own owner name in
1566  // the locks. Since these names are not unique (they may
1567  // just be the name of the local user running the DAV client)
1568  // we return the ILIAS user name in all other cases.
1569  if ($lock['ilias_owner'] == $ilias->account->getId())
1570  {
1571  $owner = $lock['dav_owner'];
1572  } else {
1573  $owner = $this->getLogin($lock['ilias_owner']);
1574  }
1575 
1576  // FIXME - Shouldn't we collect all locks instead of
1577  // using an arbitrary one?
1578  $result = array(
1579  "type" => "write",
1580  "obj_id" => $lock['obj_id'],
1581  "node_id" => $lock['node_id'],
1582  "scope" => $lock['scope'],
1583  "depth" => $lock['depth'],
1584  "owner" => $owner,
1585  "token" => $lock['token'],
1586  "expires" => $lock['expires']
1587  );
1588  if ($lock['scope'] == 'exclusive')
1589  {
1590  // If there is an exclusive lock in the path, it
1591  // takes precedence over all non-exclusive locks in
1592  // parent nodes. Therefore we can can finish collecting
1593  // locks.
1594  break;
1595  }
1596  }
1597  }
1598  }
1599  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1600 
1601  return $result;
1602  }
1603 
1608  protected function getLogin($userId)
1609  {
1610  $login = ilObjUser::_lookupLogin($userId);
1611  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1612  return $login;
1613  }
1614 
1615 
1622  private function getObject($davPath)
1623  {
1624  global $tree;
1625 
1626 
1627  // If the second path elements starts with 'file_', the following
1628  // characters of the path element directly identify the ref_id of
1629  // a file object.
1630  $davPathComponents = split('/',substr($davPath,1));
1631  if (count($davPathComponents) > 1 &&
1632  substr($davPathComponents[1],0,5) == 'file_')
1633  {
1634  $ref_id = substr($davPathComponents[1],5);
1635  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1636 
1637  // Poor IE needs this, in order to successfully display
1638  // PDF documents
1639  header('Pragma: private');
1640  }
1641  else
1642  {
1643  $nodePath = $this->toNodePath($davPath);
1644  if ($nodePath == null && count($davPathComponents) == 1)
1645  {
1646  return ilObjectDAV::createObject(-1,'mountPoint');
1647  }
1648  }
1649  if (is_null($nodePath))
1650  {
1651  return null;
1652  } else {
1653  $top = $nodePath[count($nodePath) - 1];
1654  return ilObjectDAV::createObject($top['child'],$top['type']);
1655  }
1656  }
1663  private function toObjectPath($davPath)
1664  {
1665  $this->writelog('toObjectPath('.$davPath);
1666  global $tree;
1667 
1668  $nodePath = $this->toNodePath($davPath);
1669 
1670  if (is_null($nodePath))
1671  {
1672  return null;
1673  } else {
1674  $objectPath = array();
1675  foreach ($nodePath as $node)
1676  {
1677  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1678  if (is_null($pathElement))
1679  {
1680  break;
1681  }
1682  $objectPath[] = $pathElement;
1683  }
1684  return $objectPath;
1685  }
1686  }
1687 
1699  public function toNodePath($davPath)
1700  {
1701  global $tree;
1702  $this->writelog('toNodePath('.$davPath.')...');
1703 
1704  // Split the davPath into path titles
1705  $titlePath = split('/',substr($davPath,1));
1706 
1707  // Remove the client id from the beginning of the title path
1708  if (count($titlePath) > 0)
1709  {
1710  array_shift($titlePath);
1711  }
1712 
1713  // If the last path title is empty, remove it
1714  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1715  {
1716  array_pop($titlePath);
1717  }
1718 
1719  // If the path is empty, return null
1720  if (count($titlePath) == 0)
1721  {
1722  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1723  return null;
1724  }
1725 
1726  // If the path is an absolute path, ref_id is null.
1727  $ref_id = null;
1728 
1729  // If the path is a relative folder path, convert it into an absolute path
1730  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1731  {
1732  $ref_id = substr($titlePath[0],4);
1733  array_shift($titlePath);
1734  }
1735 
1736  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1737 
1738  $this->writelog('toNodePath():'.var_export($nodePath,true));
1739  return $nodePath;
1740  }
1741 
1748  private function davDeslashify($path)
1749  {
1751 
1752  if ($path[strlen($path)-1] == '/') {
1753  $path = substr($path,0, strlen($path) - 1);
1754  }
1755  $this->writelog('davDeslashify:'.$path);
1756  return $path;
1757  }
1758 
1765  private function davBasename($path)
1766  {
1767  $components = split('/',$path);
1768  return count($components) == 0 ? '' : $components[count($components) - 1];
1769  }
1770 
1777  protected function writelog($message)
1778  {
1779  // Only write log message, if we are in debug mode
1780  if ($this->isDebug)
1781  {
1782  global $ilLog, $ilias;
1783  if ($ilLog)
1784  {
1785  if ($message == '---')
1786  {
1787  $ilLog->write('');
1788  } else {
1789  $ilLog->write(
1790  $ilias->account->getLogin()
1791  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1792  .' ilDAVServer.'.str_replace("\n",";",$message)
1793  );
1794  }
1795  }
1796  else
1797  {
1798  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1799  fwrite($fh, date('Y-m-d H:i:s '));
1800  fwrite($fh, str_replace("\n",";",$message));
1801  fwrite($fh, "\n\n");
1802  fclose($fh);
1803  }
1804  }
1805  }
1806 
1819  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1820  {
1821  if ($genericURI) {
1822  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1823  $query = null;
1824  } else if ($this->clientOS == 'windows') {
1825  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1826  $query = 'mount-instructions';
1827  } else if ($this->clientBrowser == 'konqueror') {
1828  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1829  $query = null;
1830  } else if ($this->clientBrowser == 'nautilus') {
1831  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1832  $query = null;
1833  } else {
1834  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1835  $query = 'mount-instructions';
1836  }
1837  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1838  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1839 
1840  $uri = $baseUri.'/ref_'.$refId.'/';
1841  if ($query != null)
1842  {
1843  $uri .= '?'.$query;
1844  }
1845 
1846  return $uri;
1847  }
1862  function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1863  {
1864  if ($this->clientOS == 'windows') {
1865  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1866  $query = null;
1867  } else if ($this->clientBrowser == 'konqueror') {
1868  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1869  $query = null;
1870  } else if ($this->clientBrowser == 'nautilus') {
1871  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1872  $query = null;
1873  } else {
1874  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1875  $query = null;
1876  }
1877  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1878  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1879 
1880  $uri = $baseUri.'/ref_'.$refId.'/';
1881  if ($query != null)
1882  {
1883  $uri .= '?'.$query;
1884  }
1885 
1886  return $uri;
1887  }
1899  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1900  {
1901  $nodeId = 0;
1902  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1903  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1904  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1905 
1906  if (! is_null($ressourceName) && ! is_null($parentRefId))
1907  {
1908  // Quickly create URI from the known data without needing SQL queries
1909  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1910  } else {
1911  // Create URI and use some SQL queries to get the missing data
1912  global $tree;
1913  $nodePath = $tree->getNodePath($refId);
1914 
1915  if (is_null($nodePath) || count($nodePath) < 2)
1916  {
1917  // No object path? Return null - file is not in repository.
1918  $uri = null;
1919  } else {
1920  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1921  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1922  }
1923  }
1924  return $uri;
1925  }
1926 
1944  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1945  {
1946  $nodeId = 0;
1947  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1948  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1949  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1950 
1951  if (! is_null($ressourceName) && ! is_null($parentRefId))
1952  {
1953  // Quickly create URI from the known data without needing SQL queries
1954  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1955  } else {
1956  // Create URI and use some SQL queries to get the missing data
1957  global $tree;
1958  $nodePath = $tree->getNodePath($refId);
1959 
1960  if (is_null($nodePath) || count($nodePath) < 2)
1961  {
1962  // No object path? Return null - file is not in repository.
1963  $uri = null;
1964  } else {
1965  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1966  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1967  }
1968  }
1969  return $uri;
1970  }
1971 
1977  public function isWebDAVoverHTTPS() {
1978  if ($this->isHTTPS == null) {
1979  global $ilSetting;
1980  require_once 'classes/class.ilHTTPS.php';
1981  $https = new ilHTTPS();
1982  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1983  }
1984  return $this->isHTTPS;
1985  }
1986 
1995  public static function _isActive()
1996  {
1997  global $ilClientIniFile;
1998  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1999  @include_once("Auth/HTTP.php");
2000  }
2006  public static function _isActionsVisible()
2007  {
2008  global $ilClientIniFile;
2009  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
2010  }
2011 
2021  public static function _getDefaultWebfolderInstructions()
2022  {
2023  global $lng;
2024  return $lng->txt('webfolder_instructions_text');
2025  }
2026 
2053  public static function _getWebfolderInstructionsFor($webfolderTitle,
2054  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2055  $os = 'unknown', $osFlavor = 'unknown')
2056  {
2057  global $ilSetting;
2058 
2059  $settings = new ilSetting('file_access');
2060  $str = $settings->get('custom_webfolder_instructions', '');
2061  if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2062  {
2064  }
2065 
2066  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2067  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2068  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2069  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2070  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2071  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2072 
2073  switch ($os)
2074  {
2075  case 'windows' :
2076  $operatingSystem = 'WINDOWS';
2077  break;
2078  case 'unix' :
2079  switch ($osFlavor)
2080  {
2081  case 'osx' :
2082  $operatingSystem = 'MAC';
2083  break;
2084  case 'linux' :
2085  $operatingSystem = 'LINUX';
2086  break;
2087  default :
2088  $operatingSystem = 'LINUX';
2089  break;
2090  }
2091  break;
2092  default :
2093  $operatingSystem = 'UNKNOWN';
2094  break;
2095  }
2096 
2097  if ($operatingSystem != 'UNKNOWN')
2098  {
2099  $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2100  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2101  }
2102  else
2103  {
2104  $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2105  }
2106  return $str;
2107  }
2108 
2114  private function getUploadMaxFilesize() {
2115  $val = ini_get('upload_max_filesize');
2116 
2117  $val = trim($val);
2118  $last = strtolower($val[strlen($val)-1]);
2119  switch($last) {
2120  // The 'G' modifier is available since PHP 5.1.0
2121  case 'g':
2122  $val *= 1024;
2123  case 'm':
2124  $val *= 1024;
2125  case 'k':
2126  $val *= 1024;
2127  }
2128 
2129  return $val;
2130  }
2131 }
2132 // END WebDAV
2133 ?>