ILIAS  Release_5_0_x_branch Revision 61816
 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  if($this->putObjDAV->getResourceType()==""){
1012  $vir = ilUtil::virusHandling($this->putObjDAV->obj->getDirectory($this->putObjDAV->obj->version).'/'.$this->putObjDAV->obj->filename, $this->putObjDAV->obj->filename);
1013  if ($vir[0] == false)
1014  {
1015  $this->writelog('PUTfinished Virus found: '.$vir[1]);
1016  //delete file
1018  return false;
1019  }
1020  }
1021 
1022  // Update the content length in the file object, if the
1023  // the client did not specify a content_length
1024  if ($options['content_length'] == null)
1025  {
1026  $objDAV = $this->putObjDAV;
1027  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1028  $objDAV->write();
1029  $this->putObjDAV = null;
1030  }
1031  return true;
1032  }
1033 
1034 
1041  public function MKCOL($options)
1042  {
1043  global $ilUser;
1044 
1045  $this->writelog('MKCOL('.var_export($options, true).')');
1046  $this->writelog('MKCOL '.$options['path']);
1047 
1048  $path =& $this->davDeslashify($options['path']);
1049  $parent =& dirname($path);
1050  $name =& $this->davBasename($path);
1051 
1052  // No body parsing yet
1053  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1054  return "415 Unsupported media type";
1055  }
1056 
1057  // Check if an object with the path already exists.
1058  $objDAV =& $this->getObject($path);
1059  if (! is_null($objDAV))
1060  {
1061  return '405 Method not allowed';
1062  }
1063 
1064  // get parent dav object for path
1065  $parentDAV =& $this->getObject($parent);
1066 
1067  // sanity check
1068  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1069  {
1070  return '409 Conflict';
1071  }
1072 
1073  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1074  {
1075  return '403 Forbidden';
1076  }
1077 
1078  // XXX Implement code that Handles null resource here
1079 
1080  $objDAV = $parentDAV->createCollection($name);
1081 
1082  if ($objDAV != null)
1083  {
1084  // Record write event
1085  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1086  }
1087 
1088  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1089  return $result;
1090  }
1091 
1092 
1099  public function DELETE($options)
1100  {
1101  global $ilUser;
1102 
1103  $this->writelog('DELETE('.var_export($options, true).')');
1104  $this->writelog('DELETE '.$options['path']);
1105 
1106  // get dav object for path
1107  $path =& $this->davDeslashify($options['path']);
1108  $parentDAV =& $this->getObject(dirname($path));
1109  $objDAV =& $this->getObject($path);
1110 
1111  // sanity check
1112  if (is_null($objDAV) || $objDAV->isNullResource())
1113  {
1114  return '404 Not Found';
1115  }
1116  if (! $objDAV->isPermitted('delete'))
1117  {
1118  return '403 Forbidden';
1119  }
1120 
1121  $parentDAV->remove($objDAV);
1122 
1123  // Record write event
1124  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1125 
1126  return '204 No Content';
1127  }
1128 
1135  public function MOVE($options)
1136  {
1137  global $ilUser;
1138 
1139  $this->writelog('MOVE('.var_export($options, true).')');
1140  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1141 
1142  // Get path names
1143  $src = $this->davDeslashify($options['path']);
1144  $srcParent = dirname($src);
1145  $srcName = $this->davBasename($src);
1146  $dst = $this->davDeslashify($options['dest']);
1147 
1148  $dstParent = dirname($dst);
1149  $dstName = $this->davBasename($dst);
1150  $this->writelog('move '.$dst.' dstname='.$dstName);
1151  // Source and destination must not be the same
1152  if ($src == $dst)
1153  {
1154  return '409 Conflict (source and destination are the same)';
1155  }
1156 
1157  // Destination must not be in a subtree of source
1158  if (substr($dst,strlen($src)+1) == $src.'/')
1159  {
1160  return '409 Conflict (destination is in subtree of source)';
1161  }
1162 
1163  // Get dav objects for path
1164  $srcDAV =& $this->getObject($src);
1165  $dstDAV =& $this->getObject($dst);
1166  $srcParentDAV =& $this->getObject($srcParent);
1167  $dstParentDAV =& $this->getObject($dstParent);
1168 
1169  // Source must exist
1170  if ($srcDAV == null)
1171  {
1172  return '409 Conflict (source does not exist)';
1173  }
1174 
1175  // Overwriting is only allowed, if overwrite option is set to 'T'
1176  $isOverwritten = false;
1177  if ($dstDAV != null)
1178  {
1179  if ($options['overwrite'] == 'T')
1180  {
1181  // Delete the overwritten destination
1182  if ($dstDAV->isPermitted('delete'))
1183  {
1184  $dstParentDAV->remove($dstDAV);
1185  $dstDAV = null;
1186  $isOverwritten = true;
1187  } else {
1188  return '403 Not Permitted';
1189  }
1190  } else {
1191  return '412 Precondition Failed';
1192  }
1193  }
1194 
1195  // Parents of destination must exist
1196  if ($dstParentDAV == null)
1197  {
1198  return '409 Conflict (parent of destination does not exist)';
1199  }
1200 
1201  if ($srcParent == $dstParent)
1202  {
1203  // Rename source, if source and dest are in same parent
1204 
1205  // Check permission
1206  if (! $srcDAV->isPermitted('write'))
1207  {
1208  return '403 Forbidden';
1209  }
1210  $this->writelog('rename dstName='.$dstName);
1211  $srcDAV->setResourceName($dstName);
1212  $srcDAV->write();
1213  } else {
1214  // Move source, if source and dest are in same parent
1215 
1216 
1217  if (! $srcDAV->isPermitted('delete'))
1218  {
1219  return '403 Forbidden';
1220  }
1221 
1222  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1223  {
1224  return '403 Forbidden';
1225  }
1226  $dstParentDAV->addMove($srcDAV, $dstName);
1227  }
1228 
1229  // Record write event
1230  if ($isOverwritten)
1231  {
1232  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1233  }
1234  else
1235  {
1236  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1237  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1238  }
1239 
1240  return ($isOverwritten) ? '204 No Content' : '201 Created';
1241  }
1242 
1249  public function COPY($options, $del=false)
1250  {
1251  global $ilUser;
1252  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1253  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1254 
1255  // no copying to different WebDAV Servers
1256  if (isset($options["dest_url"])) {
1257  return "502 bad gateway";
1258  }
1259 
1260  $src = $this->davDeslashify($options['path']);
1261  $srcParent = dirname($src);
1262  $srcName = $this->davBasename($src);
1263  $dst = $this->davDeslashify($options['dest']);
1264  $dstParent = dirname($dst);
1265  $dstName = $this->davBasename($dst);
1266 
1267  // sanity check
1268  if ($src == $dst)
1269  {
1270  return '409 Conflict'; // src and dst are the same
1271  }
1272 
1273  if (substr($dst,strlen($src)+1) == $src.'/')
1274  {
1275  return '409 Conflict'; // dst is in subtree of src
1276  }
1277 
1278  $this->writelog('COPY src='.$src.' dst='.$dst);
1279  // get dav object for path
1280  $srcDAV =& $this->getObject($src);
1281  $dstDAV =& $this->getObject($dst);
1282  $dstParentDAV =& $this->getObject($dstParent);
1283 
1284  if (is_null($srcDAV) || $srcDAV->isNullResource())
1285  {
1286  return '409 Conflict'; // src does not exist
1287  }
1288  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1289  {
1290  return '409 Conflict'; // parent of dst does not exist
1291  }
1292  $isOverwritten = false;
1293 
1294  // XXX Handle nulltype for dstDAV
1295  if (! is_null($dstDAV))
1296  {
1297  if ($options['overwrite'] == 'T')
1298  {
1299  if ($dstDAV->isPermitted('delete'))
1300  {
1301  $dstParentDAV->remove($dstDAV);
1302  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1303 
1304  $dstDAV = null;
1305  $isOverwritten = true;
1306  } else {
1307  return '403 Forbidden';
1308  }
1309  } else {
1310  return '412 Precondition Failed';
1311  }
1312  }
1313 
1314  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1315  {
1316  return '403 Forbidden';
1317  }
1318  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1319 
1320  // Record write event
1321  ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1322  $srcDAV->getObjectId(), $ilUser->getId());
1323  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1324 
1325  return ($isOverwritten) ? '204 No Content' : '201 Created';
1326  }
1327 
1334  public function PROPPATCH(&$options)
1335  {
1336  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1337  $this->writelog('PROPPATCH '.$options['path']);
1338 
1339  // get dav object for path
1340  $path =& $this->davDeslashify($options['path']);
1341  $objDAV =& $this->getObject($path);
1342 
1343  // sanity check
1344  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1345 
1346  $isPermitted = $objDAV->isPermitted('write');
1347  foreach($options['props'] as $key => $prop) {
1348  if (!$isPermitted || $prop['ns'] == 'DAV:')
1349  {
1350  $options['props'][$key]['status'] = '403 Forbidden';
1351  } else {
1352  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1353  }
1354  }
1355 
1356  return "";
1357  }
1358 
1359 
1366  public function LOCK(&$options)
1367  {
1368  global $ilias;
1369  $this->writelog('LOCK('.var_export($options, true).')');
1370  $this->writelog('LOCK '.$options['path']);
1371 
1372  // Check if an object with the path exists.
1373  $path =& $this->davDeslashify($options['path']);
1374  $objDAV =& $this->getObject($path);
1375  // Handle null-object locking
1376  // --------------------------
1377  if (is_null($objDAV))
1378  {
1379  $this->writelog('LOCK handling null-object locking...');
1380 
1381  // If the name does not exist, we create a null-object for it
1382  if (isset($options["update"]))
1383  {
1384  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1385  return '412 Precondition Failed';
1386  }
1387 
1388  $parent = dirname($path);
1389  $parentDAV =& $this->getObject($parent);
1390  if (is_null($parentDAV))
1391  {
1392  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1393  return '404 Not Found';
1394  }
1395  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1396  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1397  {
1398  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1399  return '403 Forbidden';
1400  }
1401 
1402  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1403  $this->writelog('created null resource for '.$path);
1404  }
1405 
1406  // ---------------------
1407  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1408  {
1409  $this->writelog('LOCK lock failed - user has no write permission.');
1410  return '403 Forbidden';
1411  }
1412 
1413  // XXX - Check if there are other locks on the resource
1414  if (!isset($options['timeout']) || is_array($options['timeout']))
1415  {
1416  $options["timeout"] = time()+360; // 6min.
1417  }
1418 
1419  if(isset($options["update"])) { // Lock Update
1420  $this->writelog('LOCK update token='.var_export($options,true));
1421  $success = $this->locks->updateLockWithoutCheckingDAV(
1422  $objDAV,
1423  $options['update'],
1424  $options['timeout']
1425  );
1426  if ($success)
1427  {
1428  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1429  if ($data['ilias_owner'] == $ilias->account->getId())
1430  {
1431  $owner = $data['dav_owner'];
1432  } else {
1433  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1434  }
1435  $options['owner'] = $owner;
1436  $options['locktoken'] = $data['token'];
1437  $options['timeout'] = $data['expires'];
1438  $options['depth'] = $data['depth'];
1439  $options['scope'] = $data['scope'];
1440  $options['type'] = $data['scope'];
1441  }
1442 
1443  } else {
1444  $this->writelog('LOCK create new lock');
1445 
1446  // XXX - Attempting to create a recursive exclusive lock
1447  // on a collection must fail, if any of nodes in the subtree
1448  // of the collection already has a lock.
1449  // XXX - Attempting to create a recursive shared lock
1450  // on a collection must fail, if any of nodes in the subtree
1451  // of the collection already has an exclusive lock.
1452  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1453  $this->writelog('lock owner='.$owner);
1454  $success = $this->locks->lockWithoutCheckingDAV(
1455  $objDAV,
1456  $ilias->account->getId(),
1457  trim($options['owner']),
1458  $options['locktoken'],
1459  $options['timeout'],
1460  $options['depth'],
1461  $options['scope']
1462  );
1463  }
1464 
1465  // Note: As a workaround for the Microsoft WebDAV Client, we return
1466  // true/false here (resulting in the status '200 OK') instead of
1467  // '204 No Content').
1468  //return ($success) ? '204 No Content' : false;
1469  return $success;
1470  }
1471 
1478  public function UNLOCK(&$options)
1479  {
1480  global $log, $ilias;
1481  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1482  $this->writelog('UNLOCK '.$options['path']);
1483 
1484  // Check if an object with the path exists.
1485  $path =& $this->davDeslashify($options['path']);
1486  $objDAV =& $this->getObject($path);
1487  if (is_null($objDAV)) {
1488  return '404 Not Found';
1489  }
1490  if (! $objDAV->isPermitted('write')) {
1491  return '403 Forbidden';
1492  }
1493 
1494  $success = $this->locks->unlockWithoutCheckingDAV(
1495  $objDAV,
1496  $options['token']
1497  );
1498 
1499  // Delete null resource object if there are no locks associated to
1500  // it anymore
1501  if ($objDAV->isNullResource()
1502  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1503  {
1504  $parent = dirname($this->davDeslashify($options['path']));
1505  $parentDAV =& $this->getObject($parent);
1506  $parentDAV->remove($objDAV);
1507  }
1508 
1509  // Workaround for Mac OS X: We must return 200 here instead of
1510  // 204.
1511  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1512  return ($success) ? '200 OK' : '412 Precondition Failed';
1513  }
1514 
1527  protected function checkLock($path)
1528  {
1529  global $ilias;
1530 
1531  $this->writelog('checkLock('.$path.')');
1532  $result = null;
1533 
1534  // get dav object for path
1535  //$objDAV = $this->getObject($path);
1536 
1537  // convert DAV path into ilObjectDAV path
1538  $objPath = $this->toObjectPath($path);
1539  if (! is_null($objPath))
1540  {
1541  $objDAV = $objPath[count($objPath) - 1];
1542  $locks = $this->locks->getLocksOnPathDAV($objPath);
1543  foreach ($locks as $lock)
1544  {
1545  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1546  && $lock['node_id'] == $objDAV->getNodeId();
1547 
1548  // Check all locks on last object in path,
1549  // but only locks with depth infinity on parent objects.
1550  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1551  {
1552  // DAV Clients expects to see their own owner name in
1553  // the locks. Since these names are not unique (they may
1554  // just be the name of the local user running the DAV client)
1555  // we return the ILIAS user name in all other cases.
1556  if ($lock['ilias_owner'] == $ilias->account->getId())
1557  {
1558  $owner = $lock['dav_owner'];
1559  } else {
1560  $owner = $this->getLogin($lock['ilias_owner']);
1561  }
1562 
1563  // FIXME - Shouldn't we collect all locks instead of
1564  // using an arbitrary one?
1565  $result = array(
1566  "type" => "write",
1567  "obj_id" => $lock['obj_id'],
1568  "node_id" => $lock['node_id'],
1569  "scope" => $lock['scope'],
1570  "depth" => $lock['depth'],
1571  "owner" => $owner,
1572  "token" => $lock['token'],
1573  "expires" => $lock['expires']
1574  );
1575  if ($lock['scope'] == 'exclusive')
1576  {
1577  // If there is an exclusive lock in the path, it
1578  // takes precedence over all non-exclusive locks in
1579  // parent nodes. Therefore we can can finish collecting
1580  // locks.
1581  break;
1582  }
1583  }
1584  }
1585  }
1586  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1587 
1588  return $result;
1589  }
1590 
1595  protected function getLogin($userId)
1596  {
1597  $login = ilObjUser::_lookupLogin($userId);
1598  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1599  return $login;
1600  }
1601 
1602 
1609  private function getObject($davPath)
1610  {
1611  global $tree;
1612 
1613 
1614  // If the second path elements starts with 'file_', the following
1615  // characters of the path element directly identify the ref_id of
1616  // a file object.
1617  $davPathComponents = split('/',substr($davPath,1));
1618  if (count($davPathComponents) > 1 &&
1619  substr($davPathComponents[1],0,5) == 'file_')
1620  {
1621  $ref_id = substr($davPathComponents[1],5);
1622  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1623 
1624  // Poor IE needs this, in order to successfully display
1625  // PDF documents
1626  header('Pragma: private');
1627  }
1628  else
1629  {
1630  $nodePath = $this->toNodePath($davPath);
1631  if ($nodePath == null && count($davPathComponents) == 1)
1632  {
1633  return ilObjectDAV::createObject(-1,'mountPoint');
1634  }
1635  }
1636  if (is_null($nodePath))
1637  {
1638  return null;
1639  } else {
1640  $top = $nodePath[count($nodePath) - 1];
1641  return ilObjectDAV::createObject($top['child'],$top['type']);
1642  }
1643  }
1650  private function toObjectPath($davPath)
1651  {
1652  $this->writelog('toObjectPath('.$davPath);
1653  global $tree;
1654 
1655  $nodePath = $this->toNodePath($davPath);
1656 
1657  if (is_null($nodePath))
1658  {
1659  return null;
1660  } else {
1661  $objectPath = array();
1662  foreach ($nodePath as $node)
1663  {
1664  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1665  if (is_null($pathElement))
1666  {
1667  break;
1668  }
1669  $objectPath[] = $pathElement;
1670  }
1671  return $objectPath;
1672  }
1673  }
1674 
1686  public function toNodePath($davPath)
1687  {
1688  global $tree;
1689  $this->writelog('toNodePath('.$davPath.')...');
1690 
1691  // Split the davPath into path titles
1692  $titlePath = split('/',substr($davPath,1));
1693 
1694  // Remove the client id from the beginning of the title path
1695  if (count($titlePath) > 0)
1696  {
1697  array_shift($titlePath);
1698  }
1699 
1700  // If the last path title is empty, remove it
1701  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1702  {
1703  array_pop($titlePath);
1704  }
1705 
1706  // If the path is empty, return null
1707  if (count($titlePath) == 0)
1708  {
1709  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1710  return null;
1711  }
1712 
1713  // If the path is an absolute path, ref_id is null.
1714  $ref_id = null;
1715 
1716  // If the path is a relative folder path, convert it into an absolute path
1717  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1718  {
1719  $ref_id = substr($titlePath[0],4);
1720  array_shift($titlePath);
1721  }
1722 
1723  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1724 
1725  $this->writelog('toNodePath():'.var_export($nodePath,true));
1726  return $nodePath;
1727  }
1728 
1735  private function davDeslashify($path)
1736  {
1738 
1739  if ($path[strlen($path)-1] == '/') {
1740  $path = substr($path,0, strlen($path) - 1);
1741  }
1742  $this->writelog('davDeslashify:'.$path);
1743  return $path;
1744  }
1745 
1752  private function davBasename($path)
1753  {
1754  $components = split('/',$path);
1755  return count($components) == 0 ? '' : $components[count($components) - 1];
1756  }
1757 
1764  protected function writelog($message)
1765  {
1766  // Only write log message, if we are in debug mode
1767  if ($this->isDebug)
1768  {
1769  global $ilLog, $ilias;
1770  if ($ilLog)
1771  {
1772  if ($message == '---')
1773  {
1774  $ilLog->write('');
1775  } else {
1776  $ilLog->write(
1777  $ilias->account->getLogin()
1778  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1779  .' ilDAVServer.'.str_replace("\n",";",$message)
1780  );
1781  }
1782  }
1783  else
1784  {
1785  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1786  fwrite($fh, date('Y-m-d H:i:s '));
1787  fwrite($fh, str_replace("\n",";",$message));
1788  fwrite($fh, "\n\n");
1789  fclose($fh);
1790  }
1791  }
1792  }
1793 
1806  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1807  {
1808  if ($genericURI) {
1809  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1810  $query = null;
1811  } else if ($this->clientOS == 'windows') {
1812  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1813  $query = 'mount-instructions';
1814  } else if ($this->clientBrowser == 'konqueror') {
1815  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1816  $query = null;
1817  } else if ($this->clientBrowser == 'nautilus') {
1818  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1819  $query = null;
1820  } else {
1821  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1822  $query = 'mount-instructions';
1823  }
1824  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1825  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1826 
1827  $uri = $baseUri.'/ref_'.$refId.'/';
1828  if ($query != null)
1829  {
1830  $uri .= '?'.$query;
1831  }
1832 
1833  return $uri;
1834  }
1849  function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1850  {
1851  if ($this->clientOS == 'windows') {
1852  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1853  $query = null;
1854  } else if ($this->clientBrowser == 'konqueror') {
1855  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1856  $query = null;
1857  } else if ($this->clientBrowser == 'nautilus') {
1858  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1859  $query = null;
1860  } else {
1861  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1862  $query = null;
1863  }
1864  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1865  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1866 
1867  $uri = $baseUri.'/ref_'.$refId.'/';
1868  if ($query != null)
1869  {
1870  $uri .= '?'.$query;
1871  }
1872 
1873  return $uri;
1874  }
1886  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1887  {
1888  $nodeId = 0;
1889  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1890  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1891  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1892 
1893  if (! is_null($ressourceName) && ! is_null($parentRefId))
1894  {
1895  // Quickly create URI from the known data without needing SQL queries
1896  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1897  } else {
1898  // Create URI and use some SQL queries to get the missing data
1899  global $tree;
1900  $nodePath = $tree->getNodePath($refId);
1901 
1902  if (is_null($nodePath) || count($nodePath) < 2)
1903  {
1904  // No object path? Return null - file is not in repository.
1905  $uri = null;
1906  } else {
1907  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1908  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1909  }
1910  }
1911  return $uri;
1912  }
1913 
1931  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1932  {
1933  $nodeId = 0;
1934  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1935  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1936  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1937 
1938  if (! is_null($ressourceName) && ! is_null($parentRefId))
1939  {
1940  // Quickly create URI from the known data without needing SQL queries
1941  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1942  } else {
1943  // Create URI and use some SQL queries to get the missing data
1944  global $tree;
1945  $nodePath = $tree->getNodePath($refId);
1946 
1947  if (is_null($nodePath) || count($nodePath) < 2)
1948  {
1949  // No object path? Return null - file is not in repository.
1950  $uri = null;
1951  } else {
1952  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1953  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1954  }
1955  }
1956  return $uri;
1957  }
1958 
1964  public function isWebDAVoverHTTPS() {
1965  if ($this->isHTTPS == null) {
1966  global $ilSetting;
1967  require_once './Services/Http/classes/class.ilHTTPS.php';
1968  $https = new ilHTTPS();
1969  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1970  }
1971  return $this->isHTTPS;
1972  }
1973 
1982  public static function _isActive()
1983  {
1984  global $ilClientIniFile;
1985  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1986  @include_once("Auth/HTTP.php");
1987  }
1993  public static function _isActionsVisible()
1994  {
1995  global $ilClientIniFile;
1996  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
1997  }
1998 
2008  public static function _getDefaultWebfolderInstructions()
2009  {
2010  global $lng;
2011  return $lng->txt('webfolder_instructions_text');
2012  }
2013 
2040  public static function _getWebfolderInstructionsFor($webfolderTitle,
2041  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2042  $os = 'unknown', $osFlavor = 'unknown')
2043  {
2044  global $ilSetting;
2045 
2046  $settings = new ilSetting('file_access');
2047  $str = $settings->get('custom_webfolder_instructions', '');
2048  if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2049  {
2051  }
2052  if(is_file('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm')){
2053  $str = fread(fopen('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm', "rb"),filesize('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm'));
2054  }
2055  $str=utf8_encode($str);
2056 
2057  preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2058  $refID=$matches[0][0];
2059 
2060  $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2061  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2062  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2063  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2064  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2065  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2066  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2067 
2068  if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false){
2069  $str = preg_replace('/\[IF_IEXPLORE\]((?:.|\n)*)\[\/IF_IEXPLORE\]/','\1', $str);
2070  }else{
2071  $str = preg_replace('/\[IF_NOTIEXPLORE\]((?:.|\n)*)\[\/IF_NOTIEXPLORE\]/','\1', $str);
2072  }
2073 
2074  switch ($os)
2075  {
2076  case 'windows' :
2077  $operatingSystem = 'WINDOWS';
2078  break;
2079  case 'unix' :
2080  switch ($osFlavor)
2081  {
2082  case 'osx' :
2083  $operatingSystem = 'MAC';
2084  break;
2085  case 'linux' :
2086  $operatingSystem = 'LINUX';
2087  break;
2088  default :
2089  $operatingSystem = 'LINUX';
2090  break;
2091  }
2092  break;
2093  default :
2094  $operatingSystem = 'UNKNOWN';
2095  break;
2096  }
2097 
2098  if ($operatingSystem != 'UNKNOWN')
2099  {
2100  $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2101  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2102  }
2103  else
2104  {
2105  $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2106  }
2107  return $str;
2108  }
2109 
2115  private function getUploadMaxFilesize() {
2116  $val = ini_get('upload_max_filesize');
2117 
2118  $val = trim($val);
2119  $last = strtolower($val[strlen($val)-1]);
2120  switch($last) {
2121  // The 'G' modifier is available since PHP 5.1.0
2122  case 'g':
2123  $val *= 1024;
2124  case 'm':
2125  $val *= 1024;
2126  case 'k':
2127  $val *= 1024;
2128  }
2129 
2130  return $val;
2131  }
2132 }
2133 // END WebDAV
2134 ?>