ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilDAVServer.php
Go to the documentation of this file.
1 <?php
2 // BEGIN WebDAV
3 /*
4  +-----------------------------------------------------------------------------+
5  | ILIAS open source |
6  +-----------------------------------------------------------------------------+
7  | Copyright (c) 1998-2005 ILIAS open source, University of Cologne |
8  | |
9  | This program is free software; you can redistribute it and/or |
10  | modify it under the terms of the GNU General Public License |
11  | as published by the Free Software Foundation; either version 2 |
12  | of the License, or (at your option) any later version. |
13  | |
14  | This program is distributed in the hope that it will be useful, |
15  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17  | GNU General Public License for more details. |
18  | |
19  | You should have received a copy of the GNU General Public License |
20  | along with this program; if not, write to the Free Software |
21  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22  +-----------------------------------------------------------------------------+
23 */
24 
25 require_once 'Services/WebDAV/classes/Server.php';
26 require_once 'Services/WebDAV/classes/class.ilDAVLocks.php';
27 require_once 'Services/WebDAV/classes/class.ilDAVProperties.php';
28 require_once 'Services/WebDAV/classes/class.ilObjectDAV.php';
29 
30 require_once 'Services/User/classes/class.ilObjUser.php';
31 require_once 'Services/Utilities/classes/class.ilFileUtils.php';
32 require_once 'include/Unicode/UtfNormal.php';
33 require_once 'Services/Tracking/classes/class.ilChangeEvent.php';
34 
52 {
57  private static $instance = null;
58 
64 
68  private $locks;
72  private $properties;
73 
79  private $clientOS = 'unknown';
84  private $clientOSFlavor = 'unknown';
89  private $clientBrowser = 'unknown';
90 
101  private $putObjDAV = null;
102 
109  private $isHTTPS = null;
110 
115  private $isDebug = false;
116 
124  private function __construct()
125  {
126  $this->writelog("<constructor>");
127 
128  // Initialize the WebDAV server and create
129  // locking and property support objects
130  $this->locks = new ilDAVLocks();
131  $this->properties = new ilDAVProperties();
132  //$this->locks->createTable();
133  //$this->properties->createTable();
134 
135  // Guess operating system, operating system flavor and browser of the webdav client
136  //
137  // - We need to know the operating system in order to properly
138  // hide hidden resources in directory listings.
139  //
140  // - We need the operating system flavor and the browser to
141  // properly support mounting of a webdav folder.
142  //
143  $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
144  $this->writelog('userAgent='.$userAgent);
145  if (strpos($userAgent,'windows') !== false
146  || strpos($userAgent,'microsoft') !== false)
147  {
148  $this->clientOS = 'windows';
149  if(strpos($userAgent,'nt 5.1') !== false){
150  $this->clientOSFlavor = 'xp';
151  }else{
152  $this->clientOSFlavor = 'nichtxp';
153  }
154 
155  } else if (strpos($userAgent,'darwin') !== false
156  || strpos($userAgent,'macintosh') !== false
157  || strpos($userAgent,'linux') !== false
158  || strpos($userAgent,'solaris') !== false
159  || strpos($userAgent,'aix') !== false
160  || strpos($userAgent,'unix') !== false
161  || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
162  )
163  {
164  $this->clientOS = 'unix';
165  if (strpos($userAgent,'linux') !== false)
166  {
167  $this->clientOSFlavor = 'linux';
168  }
169  else if (strpos($userAgent,'macintosh') !== false)
170  {
171  $this->clientOSFlavor = 'osx';
172  }
173  }
174  if (strpos($userAgent,'konqueror') !== false)
175  {
176  $this->clientBrowser = 'konqueror';
177  }
178  }
179 
184  public static function getInstance()
185  {
186  if(self::$instance != NULL)
187  {
188  return self::$instance;
189  }
190  return self::$instance = new ilDAVServer();
191  }
192 
196  public function tryAuthentication()
197  {
198  if($GLOBALS['DIC']['ilAuthSession']->isAuthenticated())
199  {
200  ilLoggerFactory::getLogger('init')->debug('User session is valid');
201  return true;
202  }
203 
204  ilLoggerFactory::getLogger('init')->debug('Trying http (webdav) authentication.');
205 
206  include_once './Services/Authentication/classes/Frontend/class.ilAuthFrontendCredentialsHTTP.php';
207  $credentials = new ilAuthFrontendCredentialsHTTP();
208  $credentials->initFromRequest();
209 
210  include_once './Services/Authentication/classes/Provider/class.ilAuthProviderFactory.php';
211  $provider_factory = new ilAuthProviderFactory();
212  $providers = $provider_factory->getProviders($credentials);
213 
214  include_once './Services/Authentication/classes/class.ilAuthStatus.php';
215  $status = ilAuthStatus::getInstance();
216 
217  include_once './Services/Authentication/classes/Frontend/class.ilAuthFrontendFactory.php';
218  $frontend_factory = new ilAuthFrontendFactory();
219  $frontend_factory->setContext(ilAuthFrontendFactory::CONTEXT_HTTP);
220  $frontend = $frontend_factory->getFrontend(
221  $GLOBALS['DIC']['ilAuthSession'],
222  $status,
223  $credentials,
224  $providers
225  );
226 
227  $frontend->authenticate();
228 
229  switch($status->getStatus())
230  {
232  ilLoggerFactory::getLogger('auth')->debug('Authentication successful. Serving request');
233  ilLoggerFactory::getLogger('auth')->info('Authenticated user id: ' . $GLOBALS['DIC']['ilAuthSession']->getUserId());
234  ilLoggerFactory::getLogger('auth')->debug('Auth info authenticated: ' .$GLOBALS['DIC']['ilAuthSession']->isAuthenticated());
235  ilLoggerFactory::getLogger('auth')->debug('Auth info expired: ' .$GLOBALS['DIC']['ilAuthSession']->isExpired());
236  ilInitialisation::initUserAccount();
237  return true;
238 
240  ilLoggerFactory::getLogger('auth')->debug('Authentication failed; Account migration required.');
241  return false;
242 
244  ilLoggerFactory::getLogger('auth')->debug('Authentication failed; Wrong login, password.');
245  return false;
246  }
247 
248  return false;
249  }
250 
251 
255  public function serveRequest()
256  {
257  if(!$this->tryAuthentication())
258  {
259  return false;
260  }
261 
262  // die quickly if plugin is deactivated
263  if (!self::_isActive())
264  {
265  $this->writelog(__METHOD__.' WebDAV disabled. Aborting');
266  $this->http_status('403 Forbidden');
267  echo '<html><body><h1>Sorry</h1>'.
268  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
269  '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
270  '</body></html>';
271  return;
272  }
273 
274  try {
275  $start = time();
276  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
277  parent::serveRequest();
278  $end = time();
279  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
280  $this->writelog('---');
281  }
282  catch (Exception $e)
283  {
284  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
285  }
286  }
287 
322  private function davUrlEncode($path)
323  {
324  // We compose the path to Unicode Normal Form NFC
325  // This ensures that diaeresis and other special characters
326  // are treated uniformly on Windows and on Mac OS X
328 
329  $c = explode('/',$path);
330  for ($i = 0; $i < count($c); $i++)
331  {
332  $c[$i] = str_replace('+','%20',urlencode($c[$i]));
333  }
334  return implode('/',$c);
335  }
336 
344  public function PROPFIND(&$options, &$files)
345  {
346  // Activate tree cache
347  global $tree;
348  //$tree->useCache(true);
349 
350  $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
351  $this->writelog('PROPFIND '.$options['path']);
352 
353  // get dav object for path
354  $path =& $this->davDeslashify($options['path']);
355  $objDAV =& $this->getObject($path);
356 
357  // prepare property array
358  $files['files'] = array();
359 
360  // sanity check
361  if (is_null($objDAV)) {
362  return false;
363  }
364  if (! $objDAV->isPermitted('visible,read')) {
365  return '403 Forbidden';
366  }
367 
368  // store information for the requested path itself
369  // FIXME : create display name for object.
370  $encodedPath = $this->davUrlEncode($path);
371 
372  $GLOBALS['ilLog']->write(print_r($encodedPath,true));
373 
374  $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
375 
376  // information for contained resources requested?
377  if (!empty($options['depth'])) {
378  // The breadthFirst list holds the collections which we have not
379  // processed yet. If depth is infinity we append unprocessed collections
380  // to the end of this list, and remove processed collections from
381  // the beginning of this list.
382  $breadthFirst = array($objDAV);
383  $objDAV->encodedPath = $encodedPath;
384 
385  while (count($breadthFirst) > 0) {
386  // remove a collection from the beginning of the breadthFirst list
387  $collectionDAV = array_shift($breadthFirst);
388  $childrenDAV =& $collectionDAV->childrenWithPermission('visible,read');
389  foreach ($childrenDAV as $childDAV)
390  {
391  // On duplicate names, work with the older object (the one with the
392  // smaller object id).
393  foreach ($childrenDAV as $duplChildDAV)
394  {
395  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
396  $duplChildDAV->getResourceName() == $childDAV->getResourceName())
397  {
398  continue 2;
399  }
400  }
401 
402  // only add visible objects to the file list
403  if (!$this->isFileHidden($childDAV))
404  {
405  $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
406  $files['files'][] =& $this->fileinfo(
407  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
408  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
409  $childDAV
410  );
411  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
412  // add a collection to the end of the breadthFirst list
413  $breadthFirst[] = $childDAV;
414  $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
415  }
416  }
417  }
418  }
419  }
420 
421  // Record read event but don't catch up with write events, because
422  // with WebDAV, a user can not see all objects contained in a folder.
423  global $ilUser;
424  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
425  $objDAV->getObjectId(), $ilUser->getId(), false);
426 
427  // ok, all done
428  $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
429  return true;
430  }
431 
442  private function isFileHidden(&$objDAV)
443  {
444  // Hide null resources which haven't got an active lock
445  if ($objDAV->isNullResource()) {
446  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
447  return;
448  }
449  }
450 
451  $name = $objDAV->getResourceName();
452  $isFileHidden = false;
453  switch ($this->clientOS)
454  {
455  case 'unix' :
456  // Hide Windows thumbnail files, and files which start with '~$'.
457  $isFileHidden =
458  $name == 'Thumbs.db'
459  || substr($name, 0, 2) == '~$';
460  // Hide files which contain /
461  $isFileHidden |= preg_match('/\\//', $name);
462  break;
463  case 'windows' :
464  // Hide files that start with '.'.
465  $isFileHidden = substr($name, 0, 1) == '.';
466  // Hide files which contain \ / : * ? " < > |
467  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
468  break;
469  default :
470  // Hide files which contain /
471  $isFileHidden |= preg_match('/\\//', $name);
472  break;
473  }
474  $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
475  return $isFileHidden;
476  }
477 
485  private function fileinfo($resourcePath, $displayPath, &$objDAV)
486  {
487  global $ilias;
488 
489  $this->writelog('fileinfo('.$resourcePath.')');
490  // create result array
491  $info = array();
492  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
493  * end of a resource path to a collection.
494  * However Mac OS X does not like this!
495  */
496  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
497  $info['path'] = $resourcePath.'/';
498  } else {
499  $info['path'] = $resourcePath;
500  }
501 
502  $info['props'] = array();
503 
504  // no special beautified displayname here ...
505  $info["props"][] =& $this->mkprop("displayname", $displayPath);
506 
507  // creation and modification time
508  $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
509  $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
510 
511  // directory (WebDAV collection)
512  $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
513  $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
514  $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
515 
516  // Only show supported locks for users who have write permission
517  if ($objDAV->isPermitted('write'))
518  {
519  $info["props"][] =& $this->mkprop("supportedlock",
520  '<D:lockentry>'
521  .'<D:lockscope><D:exclusive/></D:lockscope>'
522  .'<D:locktype><D:write/></D:locktype>'
523  .'</D:lockentry>'
524  .'<D:lockentry>'
525  .'<D:lockscope><D:shared/></D:lockscope>'
526  .'<D:locktype><D:write/></D:locktype>'
527  .'</D:lockentry>'
528  );
529  }
530 
531  // Maybe we should only show locks on objects for users who have write permission.
532  // But if we don't show these locks, users who have write permission in an object
533  // further down in a hierarchy can't see who is locking their object.
534  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
535  $lockdiscovery = '';
536  foreach ($locks as $lock)
537  {
538  // DAV Clients expects to see their own owner name in
539  // the locks. Since these names are not unique (they may
540  // just be the name of the local user running the DAV client)
541  // we return the ILIAS user name in all other cases.
542  if ($lock['ilias_owner'] == $ilias->account->getId())
543  {
544  $owner = $lock['dav_owner'];
545  } else {
546  $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
547  }
548  $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
549 
550  $lockdiscovery .=
551  '<D:activelock>'
552  .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
553  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
554  .'<D:locktype><D:write/></D:locktype>'
555  .'<D:depth>'.$lock['depth'].'</D:depth>'
556  .'<D:owner>'.$owner.'</D:owner>'
557 
558  // more than a million is considered an absolute timestamp
559  // less is more likely a relative value
560  .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
561  .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
562  .'</D:activelock>'
563  ;
564  }
565  if (strlen($lockdiscovery) > 0)
566  {
567  $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
568  }
569 
570  // get additional properties from database
571  $properties = $this->properties->getAll($objDAV);
572  foreach ($properties as $prop)
573  {
574  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
575  }
576 
577  //$this->writelog('fileinfo():'.var_export($info, true));
578  return $info;
579  }
580 
592  public function GET(&$options)
593  {
594  global $ilUser;
595 
596  $this->writelog('GET('.var_export($options, true).')');
597  $this->writelog('GET('.$options['path'].')');
598 
599  // get dav object for path
600  $path = $this->davDeslashify($options['path']);
601  $objDAV =& $this->getObject($path);
602 
603  // sanity check
604  if (is_null($objDAV) || $objDAV->isNullResource())
605  {
606  return false;
607  }
608 
609  if (! $objDAV->isPermitted('visible,read'))
610  {
611  return '403 Forbidden';
612  }
613 
614  // is this a collection?
615  if ($objDAV->isCollection())
616  {
617  if (isset($_GET['mount']))
618  {
619  return $this->mountDir($objDAV, $options);
620  }
621  else if (isset($_GET['mount-instructions']))
622  {
623  return $this->showMountInstructions($objDAV, $options);
624  }
625  else
626  {
627  return $this->getDir($objDAV, $options);
628  }
629  }
630  // detect content type
631  $options['mimetype'] =& $objDAV->getContentType();
632  // detect modification time
633  // see rfc2518, section 13.7
634  // some clients seem to treat this as a reverse rule
635  // requiring a Last-Modified header if the getlastmodified header was set
636  $options['mtime'] =& $objDAV->getModificationTimestamp();
637 
638  // detect content length
639  $options['size'] =& $objDAV->getContentLength();
640 
641  // get content as stream or as data array
642  $options['stream'] =& $objDAV->getContentStream();
643  if (is_null($options['stream']))
644  {
645  $options['data'] =& $objDAV->getContentData();
646  }
647 
648  // Record read event and catch up write events
649  ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
650  $objDAV->getObjectId(), $ilUser->getId());
651 
652  $this->writelog('GET:'.var_export($options, true));
653 
654  return true;
655  }
667  private function mountDir(&$objDAV, &$options)
668  {
669  $path = $this->davDeslashify($options['path']);
670 
671  header('Content-Type: application/davmount+xml');
672 
673  echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
674  echo " </dm:url>".$this->base_uri."</dm:url>\n";
675 
676  $xmlPath = str_replace('&','&amp;',$path);
677  $xmlPath = str_replace('<','&lt;',$xmlPath);
678  $xmlPath = str_replace('>','&gt;',$xmlPath);
679 
680  echo " </dm:open>$xmlPath</dm:open>\n";
681  echo "</dm:mount>\n";
682 
683  exit;
684 
685  }
692  private function showMountInstructions(&$objDAV, &$options)
693  {
694  global $lng,$ilUser;
695 
696  $path = $this->davDeslashify($options['path']);
697 
698  // The $path variable may contain a full or a shortened DAV path.
699  // We convert it into an object path, which we can then use to
700  // construct a new full DAV path.
701  $objectPath = $this->toObjectPath($path);
702 
703  // Construct a (possibly) full DAV path from the object path.
704  $fullPath = '';
705  foreach ($objectPath as $object)
706  {
707  if ($object->getRefId() == 1 && $this->isFileHidden($object))
708  {
709  // If the repository root object is hidden, we can not
710  // create a full path, because nothing would appear in the
711  // webfolder. We resort to a shortened path instead.
712  $fullPath .= '/ref_1';
713  }
714  else
715  {
716  $fullPath .= '/'.$this->davUrlEncode($object->getResourceName());
717  }
718  }
719 
720  // Construct a shortened DAV path from the object path.
721  $shortenedPath = '/ref_'.
722  $objectPath[count($objectPath) - 1]->getRefId();
723 
724  if ($objDAV->isCollection())
725  {
726  $shortenedPath .= '/';
727  $fullPath .= '/';
728  }
729 
730  // Prepend client id to path
731  $shortenedPath = '/'.CLIENT_ID.$shortenedPath;
732  $fullPath = '/'.CLIENT_ID.$fullPath;
733 
734  // Construct webfolder URI's. The URI's are used for mounting the
735  // webfolder. Since mounting using URI's is not standardized, we have
736  // to create different URI's for different browsers.
737  $webfolderURI = $this->base_uri.$shortenedPath;
738  $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav").
739  substr($this->base_uri, strrpos($this->base_uri,':')).
740  $shortenedPath;
741  ;
742  $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav").
743  substr($this->base_uri, strrpos($this->base_uri,':')).
744  $shortenedPath
745  ;
746  $webfolderURI_IE = $this->base_uri.$shortenedPath;
747 
748  $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
749 
750  header('Content-Type: text/html; charset=UTF-8');
751  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
752  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
753  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
754  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
755  echo " <head>\n";
756  echo " <title>".sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle)."</title>\n";
757  echo " </head>\n";
758  echo " <body>\n";
759 
761  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
762  $this->clientOS,$this->clientOSFlavor);
763 
764  echo " </body>\n";
765  echo "</html>\n";
766 
767  // Logout anonymous user to force authentication after calling mount uri
768  if($ilUser->getId() == ANONYMOUS_USER_ID)
769  {
770  $GOBALS['DIC']['ilAuthSession']->logout();
771  }
772 
773  exit;
774  }
784  private function getDir(&$objDAV, &$options)
785  {
786  global $ilias, $lng;
787 
788  // Activate tree cache
789  global $tree;
790  //$tree->useCache(true);
791 
792  $path = $this->davDeslashify($options['path']);
793 
794  // The URL of a directory must end with a slash.
795  // If it does not we are redirecting the browser.
796  // The slash is required, because we are using relative links in the
797  // HTML code we are generating below.
798  if ($path.'/' != $options['path'])
799  {
800  header('Location: '.$this->base_uri.$path.'/');
801  exit;
802  }
803 
804  header('Content-Type: text/html; charset=UTF-8');
805 
806  // fixed width directory column format
807  $format = "%15s %-19s %-s\n";
808 
809  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
810  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
811  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
812  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
813  echo "<head>\n";
814  echo "<title>".sprintf($lng->txt('webfolder_index_of'), $path)."</title>\n";
815 
816  // Create "anchorClick" behavior for for Internet Explorer
817  // This allows to create a link to a webfolder
818  echo "<style type=\"text/css\">\n";
819  echo "<!--\n";
820  echo "a {\n";
821  echo " behavior:url(#default#AnchorClick);\n";
822  echo "}\n";
823  echo "-->\n";
824  echo "</style>\n";
825 
826  echo "</head><body>\n";
827 
828  $hrefPath = '';
829  $pathComponents = explode('/',$path);
830  $uriComponents = array();
831  foreach ($pathComponents as $component)
832  {
833  $uriComponents[] = $this->davUrlEncode($component);
834  }
835  for ($i = 0; $i < count($pathComponents); $i++)
836  {
837  $displayName = htmlspecialchars($pathComponents[$i]);
838  if ($i != 0) {
839  $hrefPath .= '/';
840  }
841  $uriPath = implode('/', array_slice($uriComponents,0,$i + 1));
842  if ($i < 2)
843  {
844  // The first two path elements consist of the webdav.php script
845  // and the client id. These elements are not part of the
846  // directory structure and thus are not represented as links.
847  $hrefPath .= $displayName;
848  }
849  else
850  {
851  $hrefPath .= '<a href="'.$this->base_uri.$uriPath.'/">'.$displayName.'</a>';
852  }
853  }
854  echo "<h3>".sprintf($lng->txt('webfolder_index_of'), $hrefPath)."</h3>\n";
855 
856  // Display user id
857  if ($ilias->account->getLogin() == 'anonymous')
858  {
859  echo "<p><font size=\"-1\">".$lng->txt('not_logged_in')."</font><br>\n";
860  } else {
861  echo "<p><font size=\"-1\">".$lng->txt('login_as')." <i>"
862  .$ilias->account->getFirstname().' '
863  .$ilias->account->getLastname().' '
864  .' '.$ilias->account->getLogin().'</i> '
865  .', '.$lng->txt('client').' <i>'.$ilias->getClientId().'</i>.'
866  ."</font><p>\n";
867  }
868 
869  // Create "open as webfolder" link
870  $href = $this->base_uri.$uriPath;
871  // IE can not mount long paths. If the path has more than one element, we
872  // create a relative path with a ref-id.
873  if (count($pathComponents) > 2)
874  {
875  $hrefIE = $this->base_uri.'/'.CLIENT_ID.'/ref_'.$objDAV->getRefId();
876  } else {
877  $hrefIE = $href;
878  }
879  echo "<p><font size=\"-1\">".
880  sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions").
881  "</font></p>\n";
882  echo "<p><font size=\"-1\">".
883  sprintf($lng->txt('webfolder_mount_dir_with'),
884  "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
885  'webdav'.substr($href,4), // Konqueror
886  'dav'.substr($href,4), // Nautilus
887  $href.'?mount' // RFC 4709
888  )
889  ."</font></p>\n";
890 
891  echo "<pre>";
892  printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
893  echo "<hr>";
894 
895  $collectionCount = 0;
896  $fileCount = 0;
897  $children =& $objDAV->childrenWithPermission('visible,read');
898  foreach ($children as $childDAV) {
899  if ($childDAV->isCollection() && !$this->isFileHidden($childDAV))
900  {
901  $collectionCount++;
902  $name = $this->davUrlEncode($childDAV->getResourceName());
903  printf($format,
904  '-',
905  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
906  '<a href="'.$name.'/'.'">'.$childDAV->getDisplayName()."</a>");
907  }
908  }
909  foreach ($children as $childDAV) {
910  if ($childDAV->isFile() && !$this->isFileHidden($childDAV))
911  {
912  $fileCount++;
913  $name = $this->davUrlEncode($childDAV->getResourceName());
914  printf($format,
915  number_format($childDAV->getContentLength()),
916  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
917  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
918  }
919  }
920  foreach ($children as $childDAV) {
921  if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV))
922  {
923  $name = $this->davUrlEncode($childDAV->getResourceName());
924  printf($format,
925  'Lock',
926  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
927  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
928  }
929  }
930  echo "<hr>";
931  echo $collectionCount.' '.$lng->txt(($collectionCount == 1) ? 'folder' : 'folders').', ';
932  echo $fileCount.' '.$lng->txt(($fileCount == 1) ? 'file' : 'files').'.';
933  echo "</pre>";
934  echo "</body></html>\n";
935 
936  exit;
937  }
938 
939 
946  public function PUT(&$options)
947  {
948  global $ilUser;
949 
950  $this->writelog('PUT('.var_export($options, true).')');
951 
952  $path = $this->davDeslashify($options['path']);
953  $parent = dirname($path);
954  $name = $this->davBasename($path);
955 
956  //Check if FileType is allowed
957  if ($name != ilFileUtils::getValidFilename($name)) {
958  return false;
959  }
960 
961  // get dav object for path
962  $parentDAV =& $this->getObject($parent);
963 
964  // sanity check
965  if (is_null($parentDAV) || ! $parentDAV->isCollection()) {
966  return '409 Conflict';
967  }
968 
969  // Prevent putting of files which exceed upload limit
970  // FIXME: since this is an optional parameter, we should to do the
971  // same check again in function PUTfinished.
972  if ($options['content_length'] != null &&
973  $options['content_length'] > $this->getUploadMaxFilesize()) {
974 
975  $this->writelog('PUT is forbidden, because content length='.
976  $options['content_length'].' is larger than upload_max_filesize='.
977  $this->getUploadMaxFilesize().'in php.ini');
978 
979  return '403 Forbidden';
980  }
981 
982  // determine mime type
983  include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
984  $mime = ilMimeTypeUtil::lookupMimeType($name);
985 
986  $objDAV =& $this->getObject($path);
987  if (is_null($objDAV))
988  {
989  $ttype = $parentDAV->getILIASFileType();
990  $isperm = $parentDAV->isPermitted('create', $ttype);
991  if (! $isperm)
992  {
993  $this->writelog('PUT is forbidden, because user has no create permission');
994 
995  return '403 Forbidden';
996  }
997  $options["new"] = true;
998  $objDAV =& $parentDAV->createFile($name);
999  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
1000  //$objDAV->setContentType($options['content_type']);
1001  $objDAV->setContentType($mime);
1002  if ($options['content_length'] != null)
1003  {
1004  $objDAV->setContentLength($options['content_length']);
1005  }
1006  $objDAV->write();
1007  // Record write event
1008  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1009  }
1010  else if ($objDAV->isNullResource())
1011  {
1012  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
1013  {
1014  $this->writelog('PUT is forbidden, because user has no create permission');
1015  return '403 Forbidden';
1016  }
1017  $options["new"] = false;
1018  $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
1019  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
1020  //$objDAV->setContentType($options['content_type']);
1021  $objDAV->setContentType($mime);
1022  if ($options['content_length'] != null)
1023  {
1024  $objDAV->setContentLength($options['content_length']);
1025  }
1026  $objDAV->write();
1027 
1028  // Record write event
1029  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1030  }
1031  else
1032  {
1033  if (! $objDAV->isPermitted('write'))
1034  {
1035  $this->writelog('PUT is forbidden, because user has no write permission');
1036  return '403 Forbidden';
1037  }
1038  $options["new"] = false;
1039  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type'].' content_length='.$options['content_length']);
1040 
1041  // Create a new version if the previous version is not empty
1042  if ($objDAV->getContentLength() != 0) {
1043  $objDAV->createNewVersion();
1044  }
1045 
1046  //$objDAV->setContentType($options['content_type']);
1047  $objDAV->setContentType($mime);
1048  if ($options['content_length'] != null)
1049  {
1050  $objDAV->setContentLength($options['content_length']);
1051  }
1052  $objDAV->write();
1053 
1054  // Record write event
1055  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
1056  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
1057  }
1058  // store this object, we reuse it in method PUTfinished
1059  $this->putObjDAV = $objDAV;
1060 
1061  $out =& $objDAV->getContentOutputStream();
1062  $this->writelog('PUT outputstream='.$out);
1063 
1064  return $out;
1065  }
1066 
1073  public function PUTfinished(&$options)
1074  {
1075  $this->writelog('PUTfinished('.var_export($options, true).')');
1076 
1077  if($this->putObjDAV->getResourceType()==""){
1078  $vir = ilUtil::virusHandling($this->putObjDAV->obj->getDirectory($this->putObjDAV->obj->version).'/'.$this->putObjDAV->obj->filename, $this->putObjDAV->obj->filename);
1079  if ($vir[0] == false)
1080  {
1081  $this->writelog('PUTfinished Virus found: '.$vir[1]);
1082  //delete file
1084  return false;
1085  }
1086  }
1087 
1088  // Update the content length in the file object, if the
1089  // the client did not specify a content_length
1090  if ($options['content_length'] == null || $this->putObjDAV->getContentLength() == 0)
1091  {
1092  $objDAV = $this->putObjDAV;
1093  if ($objDAV->getContentOutputStreamLength() != null) {
1094  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1095  } else {
1096  $objDAV->write();
1097  $objDAV->setContentLength(filesize($objDAV->obj->getDirectory($objDAV->obj->version).'/'.$objDAV->obj->filename));
1098  }
1099  $objDAV->write();
1100  $this->putObjDAV = null;
1101  }
1102  return true;
1103  }
1104 
1105 
1112  public function MKCOL($options)
1113  {
1114  global $ilUser;
1115 
1116  $this->writelog('MKCOL('.var_export($options, true).')');
1117  $this->writelog('MKCOL '.$options['path']);
1118 
1119  $path =& $this->davDeslashify($options['path']);
1120  $parent =& dirname($path);
1121  $name =& $this->davBasename($path);
1122 
1123  // No body parsing yet
1124  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1125  return "415 Unsupported media type";
1126  }
1127 
1128  // Check if an object with the path already exists.
1129  $objDAV =& $this->getObject($path);
1130  if (! is_null($objDAV))
1131  {
1132  return '405 Method not allowed';
1133  }
1134 
1135  // get parent dav object for path
1136  $parentDAV =& $this->getObject($parent);
1137 
1138  // sanity check
1139  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1140  {
1141  return '409 Conflict';
1142  }
1143 
1144  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1145  {
1146  return '403 Forbidden';
1147  }
1148 
1149  // XXX Implement code that Handles null resource here
1150 
1151  $objDAV = $parentDAV->createCollection($name);
1152 
1153  if ($objDAV != null)
1154  {
1155  // Record write event
1156  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1157  }
1158 
1159  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1160  return $result;
1161  }
1162 
1163 
1170  public function DELETE($options)
1171  {
1172  global $ilUser;
1173 
1174  $this->writelog('DELETE('.var_export($options, true).')');
1175  $this->writelog('DELETE '.$options['path']);
1176 
1177  // get dav object for path
1178  $path =& $this->davDeslashify($options['path']);
1179  $parentDAV =& $this->getObject(dirname($path));
1180  $objDAV =& $this->getObject($path);
1181 
1182  // sanity check
1183  if (is_null($objDAV) || $objDAV->isNullResource())
1184  {
1185  return '404 Not Found';
1186  }
1187  if (! $objDAV->isPermitted('delete'))
1188  {
1189  return '403 Forbidden';
1190  }
1191 
1192  $parentDAV->remove($objDAV);
1193 
1194  // Record write event
1195  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1196 
1197  return '204 No Content';
1198  }
1199 
1206  public function MOVE($options)
1207  {
1208  global $ilUser;
1209 
1210  $this->writelog('MOVE('.var_export($options, true).')');
1211  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1212 
1213  // Get path names
1214  $src = $this->davDeslashify($options['path']);
1215  $srcParent = dirname($src);
1216  $srcName = $this->davBasename($src);
1217  $dst = $this->davDeslashify($options['dest']);
1218 
1219  $dstParent = dirname($dst);
1220  $dstName = $this->davBasename($dst);
1221  $this->writelog('move '.$dst.' dstname='.$dstName);
1222  // Source and destination must not be the same
1223  if ($src == $dst)
1224  {
1225  return '409 Conflict (source and destination are the same)';
1226  }
1227 
1228  // Destination must not be in a subtree of source
1229  if (substr($dst,strlen($src)+1) == $src.'/')
1230  {
1231  return '409 Conflict (destination is in subtree of source)';
1232  }
1233 
1234  // Get dav objects for path
1235  $srcDAV =& $this->getObject($src);
1236  $dstDAV =& $this->getObject($dst);
1237  $srcParentDAV =& $this->getObject($srcParent);
1238  $dstParentDAV =& $this->getObject($dstParent);
1239 
1240  // Source must exist
1241  if ($srcDAV == null)
1242  {
1243  return '409 Conflict (source does not exist)';
1244  }
1245 
1246  // Overwriting is only allowed, if overwrite option is set to 'T'
1247  $isOverwritten = false;
1248  if ($dstDAV != null)
1249  {
1250  if ($options['overwrite'] == 'T')
1251  {
1252  // Delete the overwritten destination
1253  if ($dstDAV->isPermitted('delete'))
1254  {
1255  $dstParentDAV->remove($dstDAV);
1256  $dstDAV = null;
1257  $isOverwritten = true;
1258  } else {
1259  return '403 Not Permitted';
1260  }
1261  } else {
1262  return '412 Precondition Failed';
1263  }
1264  }
1265 
1266  // Parents of destination must exist
1267  if ($dstParentDAV == null)
1268  {
1269  return '409 Conflict (parent of destination does not exist)';
1270  }
1271 
1272  if ($srcParent == $dstParent)
1273  {
1274  // Rename source, if source and dest are in same parent
1275 
1276  // Check permission
1277  if (! $srcDAV->isPermitted('write'))
1278  {
1279  return '403 Forbidden';
1280  }
1281  $this->writelog('rename dstName='.$dstName);
1282  $srcDAV->setResourceName($dstName);
1283  $srcDAV->write();
1284  } else {
1285  // Move source, if source and dest are in same parent
1286 
1287 
1288  if (! $srcDAV->isPermitted('delete'))
1289  {
1290  return '403 Forbidden';
1291  }
1292 
1293  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1294  {
1295  return '403 Forbidden';
1296  }
1297  $dstParentDAV->addMove($srcDAV, $dstName);
1298  }
1299 
1300  // Record write event
1301  if ($isOverwritten)
1302  {
1303  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1304  }
1305  else
1306  {
1307  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1308  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1309  }
1310 
1311  return ($isOverwritten) ? '204 No Content' : '201 Created';
1312  }
1313 
1320  public function COPY($options, $del=false)
1321  {
1322  global $ilUser;
1323  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1324  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1325 
1326  // no copying to different WebDAV Servers
1327  if (isset($options["dest_url"])) {
1328  return "502 bad gateway";
1329  }
1330 
1331  $src = $this->davDeslashify($options['path']);
1332  $srcParent = dirname($src);
1333  $srcName = $this->davBasename($src);
1334  $dst = $this->davDeslashify($options['dest']);
1335  $dstParent = dirname($dst);
1336  $dstName = $this->davBasename($dst);
1337 
1338  // sanity check
1339  if ($src == $dst)
1340  {
1341  return '409 Conflict'; // src and dst are the same
1342  }
1343 
1344  if (substr($dst,strlen($src)+1) == $src.'/')
1345  {
1346  return '409 Conflict'; // dst is in subtree of src
1347  }
1348 
1349  $this->writelog('COPY src='.$src.' dst='.$dst);
1350  // get dav object for path
1351  $srcDAV =& $this->getObject($src);
1352  $dstDAV =& $this->getObject($dst);
1353  $dstParentDAV =& $this->getObject($dstParent);
1354 
1355  if (is_null($srcDAV) || $srcDAV->isNullResource())
1356  {
1357  return '409 Conflict'; // src does not exist
1358  }
1359  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1360  {
1361  return '409 Conflict'; // parent of dst does not exist
1362  }
1363  $isOverwritten = false;
1364 
1365  // XXX Handle nulltype for dstDAV
1366  if (! is_null($dstDAV))
1367  {
1368  if ($options['overwrite'] == 'T')
1369  {
1370  if ($dstDAV->isPermitted('delete'))
1371  {
1372  $dstParentDAV->remove($dstDAV);
1373  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1374 
1375  $dstDAV = null;
1376  $isOverwritten = true;
1377  } else {
1378  return '403 Forbidden';
1379  }
1380  } else {
1381  return '412 Precondition Failed';
1382  }
1383  }
1384 
1385  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1386  {
1387  return '403 Forbidden';
1388  }
1389  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1390 
1391  // Record write event
1392  ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1393  $srcDAV->getObjectId(), $ilUser->getId());
1394  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1395 
1396  return ($isOverwritten) ? '204 No Content' : '201 Created';
1397  }
1398 
1405  public function PROPPATCH(&$options)
1406  {
1407  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1408  $this->writelog('PROPPATCH '.$options['path']);
1409 
1410  // get dav object for path
1411  $path =& $this->davDeslashify($options['path']);
1412  $objDAV =& $this->getObject($path);
1413 
1414  // sanity check
1415  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1416 
1417  $isPermitted = $objDAV->isPermitted('write');
1418  foreach($options['props'] as $key => $prop) {
1419  if (!$isPermitted || $prop['ns'] == 'DAV:')
1420  {
1421  $options['props'][$key]['status'] = '403 Forbidden';
1422  } else {
1423  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1424  }
1425  }
1426 
1427  return "";
1428  }
1429 
1430 
1437  public function LOCK(&$options)
1438  {
1439  global $ilias;
1440  $this->writelog('LOCK('.var_export($options, true).')');
1441  $this->writelog('LOCK '.$options['path']);
1442 
1443  // Check if an object with the path exists.
1444  $path =& $this->davDeslashify($options['path']);
1445  $objDAV =& $this->getObject($path);
1446  // Handle null-object locking
1447  // --------------------------
1448  if (is_null($objDAV))
1449  {
1450  $this->writelog('LOCK handling null-object locking...');
1451 
1452  // If the name does not exist, we create a null-object for it
1453  if (isset($options["update"]))
1454  {
1455  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1456  return '412 Precondition Failed';
1457  }
1458 
1459  $parent = dirname($path);
1460  $parentDAV =& $this->getObject($parent);
1461  if (is_null($parentDAV))
1462  {
1463  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1464  return '404 Not Found';
1465  }
1466  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1467  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1468  {
1469  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1470  return '403 Forbidden';
1471  }
1472 
1473  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1474  $this->writelog('created null resource for '.$path);
1475  }
1476 
1477  // ---------------------
1478  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1479  {
1480  $this->writelog('LOCK lock failed - user has no write permission.');
1481  return '403 Forbidden';
1482  }
1483 
1484  // XXX - Check if there are other locks on the resource
1485  if (!isset($options['timeout']) || is_array($options['timeout']))
1486  {
1487  $options["timeout"] = time()+360; // 6min.
1488  }
1489 
1490  if(isset($options["update"])) { // Lock Update
1491  $this->writelog('LOCK update token='.var_export($options,true));
1492  $success = $this->locks->updateLockWithoutCheckingDAV(
1493  $objDAV,
1494  $options['update'],
1495  $options['timeout']
1496  );
1497  if ($success)
1498  {
1499  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1500  if ($data['ilias_owner'] == $ilias->account->getId())
1501  {
1502  $owner = $data['dav_owner'];
1503  } else {
1504  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1505  }
1506  $options['owner'] = $owner;
1507  $options['locktoken'] = $data['token'];
1508  $options['timeout'] = $data['expires'];
1509  $options['depth'] = $data['depth'];
1510  $options['scope'] = $data['scope'];
1511  $options['type'] = $data['scope'];
1512  }
1513 
1514  } else {
1515  $this->writelog('LOCK create new lock');
1516 
1517  // XXX - Attempting to create a recursive exclusive lock
1518  // on a collection must fail, if any of nodes in the subtree
1519  // of the collection already has a lock.
1520  // XXX - Attempting to create a recursive shared lock
1521  // on a collection must fail, if any of nodes in the subtree
1522  // of the collection already has an exclusive lock.
1523  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1524  $this->writelog('lock owner='.$owner);
1525  $success = $this->locks->lockWithoutCheckingDAV(
1526  $objDAV,
1527  $ilias->account->getId(),
1528  trim($options['owner']),
1529  $options['locktoken'],
1530  $options['timeout'],
1531  $options['depth'],
1532  $options['scope']
1533  );
1534  }
1535 
1536  // Note: As a workaround for the Microsoft WebDAV Client, we return
1537  // true/false here (resulting in the status '200 OK') instead of
1538  // '204 No Content').
1539  //return ($success) ? '204 No Content' : false;
1540  return $success;
1541  }
1542 
1549  public function UNLOCK(&$options)
1550  {
1551  global $log, $ilias;
1552  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1553  $this->writelog('UNLOCK '.$options['path']);
1554 
1555  // Check if an object with the path exists.
1556  $path =& $this->davDeslashify($options['path']);
1557  $objDAV =& $this->getObject($path);
1558  if (is_null($objDAV)) {
1559  return '404 Not Found';
1560  }
1561  if (! $objDAV->isPermitted('write')) {
1562  return '403 Forbidden';
1563  }
1564 
1565  $success = $this->locks->unlockWithoutCheckingDAV(
1566  $objDAV,
1567  $options['token']
1568  );
1569 
1570  // Delete null resource object if there are no locks associated to
1571  // it anymore
1572  if ($objDAV->isNullResource()
1573  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1574  {
1575  $parent = dirname($this->davDeslashify($options['path']));
1576  $parentDAV =& $this->getObject($parent);
1577  $parentDAV->remove($objDAV);
1578  }
1579 
1580  // Workaround for Mac OS X: We must return 200 here instead of
1581  // 204.
1582  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1583  return ($success) ? '200 OK' : '412 Precondition Failed';
1584  }
1585 
1598  protected function checkLock($path)
1599  {
1600  global $ilias;
1601 
1602  $this->writelog('checkLock('.$path.')');
1603  $result = null;
1604 
1605  // get dav object for path
1606  //$objDAV = $this->getObject($path);
1607 
1608  // convert DAV path into ilObjectDAV path
1609  $objPath = $this->toObjectPath($path);
1610  if (! is_null($objPath))
1611  {
1612  $objDAV = $objPath[count($objPath) - 1];
1613  $locks = $this->locks->getLocksOnPathDAV($objPath);
1614  foreach ($locks as $lock)
1615  {
1616  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1617  && $lock['node_id'] == $objDAV->getNodeId();
1618 
1619  // Check all locks on last object in path,
1620  // but only locks with depth infinity on parent objects.
1621  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1622  {
1623  // DAV Clients expects to see their own owner name in
1624  // the locks. Since these names are not unique (they may
1625  // just be the name of the local user running the DAV client)
1626  // we return the ILIAS user name in all other cases.
1627  if ($lock['ilias_owner'] == $ilias->account->getId())
1628  {
1629  $owner = $lock['dav_owner'];
1630  } else {
1631  $owner = $this->getLogin($lock['ilias_owner']);
1632  }
1633 
1634  // FIXME - Shouldn't we collect all locks instead of
1635  // using an arbitrary one?
1636  $result = array(
1637  "type" => "write",
1638  "obj_id" => $lock['obj_id'],
1639  "node_id" => $lock['node_id'],
1640  "scope" => $lock['scope'],
1641  "depth" => $lock['depth'],
1642  "owner" => $owner,
1643  "token" => $lock['token'],
1644  "expires" => $lock['expires']
1645  );
1646  if ($lock['scope'] == 'exclusive')
1647  {
1648  // If there is an exclusive lock in the path, it
1649  // takes precedence over all non-exclusive locks in
1650  // parent nodes. Therefore we can can finish collecting
1651  // locks.
1652  break;
1653  }
1654  }
1655  }
1656  }
1657  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1658 
1659  return $result;
1660  }
1661 
1666  protected function getLogin($userId)
1667  {
1668  $login = ilObjUser::_lookupLogin($userId);
1669  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1670  return $login;
1671  }
1672 
1673 
1680  private function getObject($davPath)
1681  {
1682  global $tree;
1683 
1684  // If the second path elements starts with 'file_', the following
1685  // characters of the path element directly identify the ref_id of
1686  // a file object.
1687  $davPathComponents = explode('/',substr($davPath,1));
1688 
1689  if (count($davPathComponents) > 1 &&
1690  substr($davPathComponents[1],0,5) == 'file_')
1691  {
1692  $ref_id = substr($davPathComponents[1],5);
1693  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1694 
1695  // Poor IE needs this, in order to successfully display
1696  // PDF documents
1697  header('Pragma: private');
1698  }
1699  else
1700  {
1701  $nodePath = $this->toNodePath($davPath);
1702  if ($nodePath == null && count($davPathComponents) == 1)
1703  {
1704  return ilObjectDAV::createObject(-1,'mountPoint');
1705  }
1706  }
1707  if (is_null($nodePath))
1708  {
1709  return null;
1710  } else {
1711  $top = $nodePath[count($nodePath) - 1];
1712  return ilObjectDAV::createObject($top['child'],$top['type']);
1713  }
1714  }
1721  private function toObjectPath($davPath)
1722  {
1723  $this->writelog('toObjectPath('.$davPath);
1724  global $tree;
1725 
1726  $nodePath = $this->toNodePath($davPath);
1727 
1728  if (is_null($nodePath))
1729  {
1730  return null;
1731  } else {
1732  $objectPath = array();
1733  foreach ($nodePath as $node)
1734  {
1735  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1736  if (is_null($pathElement))
1737  {
1738  break;
1739  }
1740  $objectPath[] = $pathElement;
1741  }
1742  return $objectPath;
1743  }
1744  }
1745 
1757  public function toNodePath($davPath)
1758  {
1759  global $tree;
1760  $this->writelog('toNodePath('.$davPath.')...');
1761 
1762  // Split the davPath into path titles
1763  $titlePath = explode('/',substr($davPath,1));
1764 
1765  // Remove the client id from the beginning of the title path
1766  if (count($titlePath) > 0)
1767  {
1768  array_shift($titlePath);
1769  }
1770 
1771  // If the last path title is empty, remove it
1772  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1773  {
1774  array_pop($titlePath);
1775  }
1776 
1777  // If the path is empty, return null
1778  if (count($titlePath) == 0)
1779  {
1780  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1781  return null;
1782  }
1783 
1784  // If the path is an absolute path, ref_id is null.
1785  $ref_id = null;
1786 
1787  // If the path is a relative folder path, convert it into an absolute path
1788  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1789  {
1790  $ref_id = substr($titlePath[0],4);
1791  array_shift($titlePath);
1792  }
1793 
1794  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1795 
1796  $this->writelog('toNodePath():'.var_export($nodePath,true));
1797  return $nodePath;
1798  }
1799 
1806  private function davDeslashify($path)
1807  {
1809 
1810  if ($path[strlen($path)-1] == '/') {
1811  $path = substr($path,0, strlen($path) - 1);
1812  }
1813  $this->writelog('davDeslashify:'.$path);
1814  return $path;
1815  }
1816 
1823  private function davBasename($path)
1824  {
1825  $components = explode('/',$path);
1826  return count($components) == 0 ? '' : $components[count($components) - 1];
1827  }
1828 
1835  protected function writelog($message)
1836  {
1837  // Only write log message, if we are in debug mode
1838  if ($this->isDebug)
1839  {
1840  global $ilLog, $ilias;
1841  if ($ilLog)
1842  {
1843  if ($message == '---')
1844  {
1845  $ilLog->write('');
1846  } else {
1847  $ilLog->write(
1848  $ilias->account->getLogin()
1849  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1850  .' ilDAVServer.'.str_replace("\n",";",$message)
1851  );
1852  }
1853  }
1854  else
1855  {
1856  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1857  fwrite($fh, date('Y-m-d H:i:s '));
1858  fwrite($fh, str_replace("\n",";",$message));
1859  fwrite($fh, "\n\n");
1860  fclose($fh);
1861  }
1862  }
1863  }
1864 
1877  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1878  {
1879  if ($genericURI) {
1880  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1881  $query = null;
1882  } else if ($this->clientOS == 'windows') {
1883  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1884  $query = 'mount-instructions';
1885  } else if ($this->clientBrowser == 'konqueror') {
1886  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1887  $query = null;
1888  } else if ($this->clientBrowser == 'nautilus') {
1889  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1890  $query = null;
1891  } else {
1892  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1893  $query = 'mount-instructions';
1894  }
1895  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1896  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1897 
1898  $uri = $baseUri.'/ref_'.$refId.'/';
1899  if ($query != null)
1900  {
1901  $uri .= '?'.$query;
1902  }
1903 
1904  return $uri;
1905  }
1920  function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1921  {
1922  if ($this->clientOS == 'windows') {
1923  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1924  $query = null;
1925  } else if ($this->clientBrowser == 'konqueror') {
1926  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1927  $query = null;
1928  } else if ($this->clientBrowser == 'nautilus') {
1929  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1930  $query = null;
1931  } else {
1932  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1933  $query = null;
1934  }
1935  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1936  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1937 
1938  $uri = $baseUri.'/ref_'.$refId.'/';
1939  if ($query != null)
1940  {
1941  $uri .= '?'.$query;
1942  }
1943 
1944  return $uri;
1945  }
1957  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1958  {
1959  $nodeId = 0;
1960  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1961  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1962  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1963 
1964  if (! is_null($ressourceName) && ! is_null($parentRefId))
1965  {
1966  // Quickly create URI from the known data without needing SQL queries
1967  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1968  } else {
1969  // Create URI and use some SQL queries to get the missing data
1970  global $tree;
1971  $nodePath = $tree->getNodePath($refId);
1972 
1973  if (is_null($nodePath) || count($nodePath) < 2)
1974  {
1975  // No object path? Return null - file is not in repository.
1976  $uri = null;
1977  } else {
1978  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1979  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1980  }
1981  }
1982  return $uri;
1983  }
1984 
2002  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
2003  {
2004  $nodeId = 0;
2005  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
2006  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
2007  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
2008 
2009  if (! is_null($ressourceName) && ! is_null($parentRefId))
2010  {
2011  // Quickly create URI from the known data without needing SQL queries
2012  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
2013  } else {
2014  // Create URI and use some SQL queries to get the missing data
2015  global $tree;
2016  $nodePath = $tree->getNodePath($refId);
2017 
2018  if (is_null($nodePath) || count($nodePath) < 2)
2019  {
2020  // No object path? Return null - file is not in repository.
2021  $uri = null;
2022  } else {
2023  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
2024  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
2025  }
2026  }
2027  return $uri;
2028  }
2029 
2035  public function isWebDAVoverHTTPS() {
2036  if ($this->isHTTPS == null) {
2037  global $ilSetting;
2038  require_once './Services/Http/classes/class.ilHTTPS.php';
2039  $https = new ilHTTPS();
2040  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
2041  }
2042  return $this->isHTTPS;
2043  }
2044 
2053  public static function _isActive()
2054  {
2055  global $ilClientIniFile;
2056  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1';
2057  }
2063  public static function _isActionsVisible()
2064  {
2065  global $ilClientIniFile;
2066  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
2067  }
2068 
2078  public static function _getDefaultWebfolderInstructions()
2079  {
2080  global $lng;
2081  return $lng->txt('webfolder_instructions_text');
2082  }
2083 
2110  public static function _getWebfolderInstructionsFor($webfolderTitle,
2111  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2112  $os = 'unknown', $osFlavor = 'unknown')
2113  {
2114  global $ilSetting;
2115 
2116  $settings = new ilSetting('file_access');
2117  $str = $settings->get('custom_webfolder_instructions', '');
2118  if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2119  {
2121  }
2122  if(is_file('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm')){
2123  $str = fread(fopen('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm', "rb"),filesize('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm'));
2124  }
2125  $str=utf8_encode($str);
2126 
2127  preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2128  $refID=end($matches[0]);
2129 
2130  $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2131  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2132  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2133  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2134  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2135  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2136  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2137 
2138  if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false){
2139  $str = preg_replace('/\[IF_IEXPLORE\](?:(.*))\[\/IF_IEXPLORE\]/s','\1', $str);
2140  }else{
2141  $str = preg_replace('/\[IF_NOTIEXPLORE\](?:(.*))\[\/IF_NOTIEXPLORE\]/s','\1', $str);
2142  }
2143 
2144  switch ($os)
2145  {
2146  case 'windows' :
2147  $operatingSystem = 'WINDOWS';
2148  break;
2149  case 'unix' :
2150  switch ($osFlavor)
2151  {
2152  case 'osx' :
2153  $operatingSystem = 'MAC';
2154  break;
2155  case 'linux' :
2156  $operatingSystem = 'LINUX';
2157  break;
2158  default :
2159  $operatingSystem = 'LINUX';
2160  break;
2161  }
2162  break;
2163  default :
2164  $operatingSystem = 'UNKNOWN';
2165  break;
2166  }
2167 
2168  if ($operatingSystem != 'UNKNOWN')
2169  {
2170  $str = preg_replace('/\[IF_'.$operatingSystem.'\](?:(.*))\[\/IF_'.$operatingSystem.'\]/s','\1', $str);
2171  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(.*))\[\/IF_\1\]/s','', $str);
2172  }
2173  else
2174  {
2175  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(.*))\[\/IF_\1\]/s','\2', $str);
2176  }
2177  return $str;
2178  }
2179 
2185  private function getUploadMaxFilesize() {
2186  $val = ini_get('upload_max_filesize');
2187 
2188  $val = trim($val);
2189  $last = strtolower($val[strlen($val)-1]);
2190  switch($last) {
2191  // The 'G' modifier is available since PHP 5.1.0
2192  case 'g':
2193  $val *= 1024;
2194  case 'm':
2195  $val *= 1024;
2196  case 'k':
2197  $val *= 1024;
2198  }
2199 
2200  return $val;
2201  }
2202 }
2203 // END WebDAV
2204 ?>
$files
Definition: add-vimline.php:18
static _isActionsVisible()
Static getter.
static _lookupLogin($a_user_id)
lookup login
static createObject($refId, $type)
Static factory method to create a DAV object for a given refId and type.
$clientBrowser
The name of some well known browsers, that need special support.
ILIAS Setting Class.
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
$result
$_GET["client_id"]
static lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=false)
$isDebug
The WebDAVServer prints lots of log messages to the ilias log, if this variable is set to true...
const STATUS_AUTHENTICATION_FAILED
DELETE($options)
DELETE method handler.
MOVE($options)
MOVE method handler.
showMountInstructions(&$objDAV, &$options)
Mount instructions method handler for directories.
static _recordWriteEvent($obj_id, $usr_id, $action, $parent_obj_id=null)
Records a write event.
mountDir(&$objDAV, &$options)
Mount method handler for directories.
getFileURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a file object using WebDAV.
Factory for auth frontend classes.
static _getDefaultWebfolderInstructions()
Gets instructions for the usage of webfolders.
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
static _isActive()
Static getter.
Virtual base class for implementing WebDAV servers.
Definition: Server.php:23
static toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:154
COPY($options, $del=false)
COPY method handler.
LOCK(&$options)
LOCK method handler.
static _catchupWriteEvents($obj_id, $usr_id, $timestamp=null)
Catches up with all write events which occured before the specified timestamp.
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1924
checkLock($path)
checkLock() helper
GET(&$options)
GET method handler.
PUTfinished(&$options)
PUTfinished handler.
$clientOSFlavor
The flavor of the operating system of the WebDAV client.
$info
Definition: example_052.php:80
serveRequest()
Serves a WebDAV request.
HTTPS.
$cachedObjectDAV
Cached object handler.
$clientOS
The operating system of the WebDAV client.
$success
Definition: Utf8Test.php:86
writelog($message)
Writes a message to the logfile.,.
if(!is_array($argv)) $options
getLogin($userId)
Returns the login for the specified user id, or null if the user does not exist.
__construct()
Constructor.
PROPFIND(&$options, &$files)
PROPFIND method handler.
date( 'd-M-Y', $objPHPExcel->getProperties() ->getCreated())
isFileHidden(&$objDAV)
Returns true, if the resource has a file name which is hidden from the user.
mkprop()
helper for property element creation
Definition: Server.php:1551
$locks
Handler for locks.
getObjectURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a object using WebDAV by its name.
$ilUser
Definition: imgupload.php:18
$https
Definition: imgupload.php:19
static _recordReadEvent($a_type, $a_ref_id, $obj_id, $usr_id, $isCatchupWriteEvents=true, $a_ext_rc=false, $a_ext_time=false)
Records a read event and catches up with write events.
Add a drawing to the header
Definition: 04printing.php:69
fileinfo($resourcePath, $displayPath, &$objDAV)
Creates file info properties for a single file/resource.
static getInstance()
Get singelton instance.
Create styles array
The data for the language used.
tryAuthentication()
Try authentication.
MKCOL($options)
MKCOL method handler.
davBasename($path)
Private implementation of PHP basename() function.
davUrlEncode($path)
We do not implement this method, because authentication is done by ilias3/webdav.php.
PUT(&$options)
PUT method handler.
getDir(&$objDAV, &$options)
GET method handler for directories.
PROPPATCH(&$options)
PROPPATCH method handler.
$ref_id
Definition: sahs_server.php:39
toNodePath($davPath)
Converts a DAV path into a node path.
static getInstance()
Get status instance.
global $ilSetting
Definition: privfeed.php:17
global $lng
Definition: privfeed.php:17
$properties
Handler for properties.
static getLogger($a_component_id)
Get component logger.
Set document properties
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
davDeslashify($path)
davDeslashify - make sure path does not end in a slash
isWebDAVoverHTTPS()
Returns true, if the WebDAV server transfers data over HTTPS.
toObjectPath($davPath)
Converts a DAV path into an array of DAV objects.
getFolderURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null)
Returns an URI for mounting the repository object as a webfolder using Internet Explorer and Firefox ...
const STATUS_ACCOUNT_MIGRATION_REQUIRED
static _getWebfolderInstructionsFor($webfolderTitle, $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus, $os='unknown', $osFlavor='unknown')
Gets Webfolder mount instructions for the specified webfolder.
static getValidFilename($a_filename)
Get valid filename.
getMountURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null, $genericURI=false)
Returns an URI for mounting the repository object as a webfolder.
getUploadMaxFilesize()
Gets the maximum permitted upload filesize from php.ini in bytes.
UNLOCK(&$options)
UNLOCK method handler.
getObject($davPath)
Gets a DAV object for the specified path.