ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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  $this->clientOS = 'windows';
148  if (strpos($userAgent, 'nt 5.1') !== false) {
149  $this->clientOSFlavor = 'xp';
150  } else {
151  $this->clientOSFlavor = 'nichtxp';
152  }
153  } elseif (strpos($userAgent, 'darwin') !== false
154  || strpos($userAgent, 'macintosh') !== false
155  || strpos($userAgent, 'linux') !== false
156  || strpos($userAgent, 'solaris') !== false
157  || strpos($userAgent, 'aix') !== false
158  || strpos($userAgent, 'unix') !== false
159  || strpos($userAgent, 'gvfs') !== false // nautilus browser uses this ID
160  ) {
161  $this->clientOS = 'unix';
162  if (strpos($userAgent, 'linux') !== false) {
163  $this->clientOSFlavor = 'linux';
164  } elseif (strpos($userAgent, 'macintosh') !== false) {
165  $this->clientOSFlavor = 'osx';
166  }
167  }
168  if (strpos($userAgent, 'konqueror') !== false) {
169  $this->clientBrowser = 'konqueror';
170  }
171  }
172 
177  public static function getInstance()
178  {
179  if (self::$instance != null) {
180  return self::$instance;
181  }
182  return self::$instance = new ilDAVServer();
183  }
184 
188  public function tryAuthentication()
189  {
190  if ($GLOBALS['DIC']['ilAuthSession']->isAuthenticated()) {
191  ilLoggerFactory::getLogger('init')->debug('User session is valid');
192  return true;
193  }
194 
195  ilLoggerFactory::getLogger('init')->debug('Trying http (webdav) authentication.');
196 
197  include_once './Services/Authentication/classes/Frontend/class.ilAuthFrontendCredentialsHTTP.php';
198  $credentials = new ilAuthFrontendCredentialsHTTP();
199  $credentials->initFromRequest();
200 
201  include_once './Services/Authentication/classes/Provider/class.ilAuthProviderFactory.php';
202  $provider_factory = new ilAuthProviderFactory();
203  $providers = $provider_factory->getProviders($credentials);
204 
205  include_once './Services/Authentication/classes/class.ilAuthStatus.php';
206  $status = ilAuthStatus::getInstance();
207 
208  include_once './Services/Authentication/classes/Frontend/class.ilAuthFrontendFactory.php';
209  $frontend_factory = new ilAuthFrontendFactory();
210  $frontend_factory->setContext(ilAuthFrontendFactory::CONTEXT_HTTP);
211  $frontend = $frontend_factory->getFrontend(
212  $GLOBALS['DIC']['ilAuthSession'],
213  $status,
214  $credentials,
215  $providers
216  );
217 
218  $frontend->authenticate();
219 
220  switch ($status->getStatus()) {
222  ilLoggerFactory::getLogger('auth')->debug('Authentication successful. Serving request');
223  ilLoggerFactory::getLogger('auth')->info('Authenticated user id: ' . $GLOBALS['DIC']['ilAuthSession']->getUserId());
224  ilLoggerFactory::getLogger('auth')->debug('Auth info authenticated: ' . $GLOBALS['DIC']['ilAuthSession']->isAuthenticated());
225  ilLoggerFactory::getLogger('auth')->debug('Auth info expired: ' . $GLOBALS['DIC']['ilAuthSession']->isExpired());
226  ilInitialisation::initUserAccount();
227  return true;
228 
230  ilLoggerFactory::getLogger('auth')->debug('Authentication failed; Account migration required.');
231  return false;
232 
234  ilLoggerFactory::getLogger('auth')->debug('Authentication failed; Wrong login, password.');
235  return false;
236  }
237 
238  return false;
239  }
240 
241 
245  public function serveRequest()
246  {
247  if (!$this->tryAuthentication()) {
248  return false;
249  }
250 
251  // die quickly if plugin is deactivated
252  if (!self::_isActive()) {
253  $this->writelog(__METHOD__ . ' WebDAV disabled. Aborting');
254  $this->http_status('403 Forbidden');
255  echo '<html><body><h1>Sorry</h1>' .
256  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>' .
257  '<p>You can only access this page, if WebDAV is enabled on this server.</p>' .
258  '</body></html>';
259  return;
260  }
261 
262  try {
263  $start = time();
264  $this->writelog('serveRequest():' . $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['PATH_INFO'] . ' ...');
265  parent::serveRequest();
266  $end = time();
267  $this->writelog('serveRequest():' . $_SERVER['REQUEST_METHOD'] . ' done status=' . $this->_http_status . ' elapsed=' . ($end - $start));
268  $this->writelog('---');
269  } catch (Exception $e) {
270  $this->writelog('serveRequest():' . $_SERVER['REQUEST_METHOD'] . ' caught exception: ' . $e->getMessage() . '\n' . $e->getTraceAsString());
271  }
272  }
273 
308  private function davUrlEncode($path)
309  {
310  // We compose the path to Unicode Normal Form NFC
311  // This ensures that diaeresis and other special characters
312  // are treated uniformly on Windows and on Mac OS X
314 
315  $c = explode('/', $path);
316  for ($i = 0; $i < count($c); $i++) {
317  $c[$i] = str_replace('+', '%20', urlencode($c[$i]));
318  }
319  return implode('/', $c);
320  }
321 
329  public function PROPFIND(&$options, &$files)
330  {
331  // Activate tree cache
332  global $tree;
333  //$tree->useCache(true);
334 
335  $this->writelog('PROPFIND(options:' . var_export($options, true) . ' files:' . var_export($files, true) . '.)');
336  $this->writelog('PROPFIND ' . $options['path']);
337 
338  // get dav object for path
339  $path =&$this->davDeslashify($options['path']);
340  $objDAV =&$this->getObject($path);
341 
342  // prepare property array
343  $files['files'] = array();
344 
345  // sanity check
346  if (is_null($objDAV)) {
347  return false;
348  }
349  if (!$objDAV->isPermitted('visible,read')) {
350  return '403 Forbidden';
351  }
352 
353  // store information for the requested path itself
354  // FIXME : create display name for object.
355  $encodedPath = $this->davUrlEncode($path);
356 
357  $GLOBALS['ilLog']->write(print_r($encodedPath, true));
358 
359  $files['files'][] =&$this->fileinfo($encodedPath, $encodedPath, $objDAV);
360 
361  // information for contained resources requested?
362  if (!empty($options['depth'])) {
363  // The breadthFirst list holds the collections which we have not
364  // processed yet. If depth is infinity we append unprocessed collections
365  // to the end of this list, and remove processed collections from
366  // the beginning of this list.
367  $breadthFirst = array($objDAV);
368  $objDAV->encodedPath = $encodedPath;
369 
370  while (count($breadthFirst) > 0) {
371  // remove a collection from the beginning of the breadthFirst list
372  $collectionDAV = array_shift($breadthFirst);
373  $childrenDAV =&$collectionDAV->childrenWithPermission('visible,read');
374  foreach ($childrenDAV as $childDAV) {
375  // On duplicate names, work with the older object (the one with the
376  // smaller object id).
377  foreach ($childrenDAV as $duplChildDAV) {
378  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
379  $duplChildDAV->getResourceName() == $childDAV->getResourceName()) {
380  continue 2;
381  }
382  }
383 
384  // only add visible objects to the file list
385  if (!$this->isFileHidden($childDAV)) {
386  $this->writelog('PROPFIND() child ref_id=' . $childDAV->getRefId());
387  $files['files'][] =&$this->fileinfo(
388  $collectionDAV->encodedPath . '/' . $this->davUrlEncode($childDAV->getResourceName()),
389  $collectionDAV->encodedPath . '/' . $this->davUrlEncode($childDAV->getDisplayName()),
390  $childDAV
391  );
392  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
393  // add a collection to the end of the breadthFirst list
394  $breadthFirst[] = $childDAV;
395  $childDAV->encodedPath = $collectionDAV->encodedPath . '/' . $this->davUrlEncode($childDAV->getResourceName());
396  }
397  }
398  }
399  }
400  }
401 
402  // Record read event but don't catch up with write events, because
403  // with WebDAV, a user can not see all objects contained in a folder.
404  global $ilUser;
406  $objDAV->getILIASType(),
407  $objDAV->getRefId(),
408  $objDAV->getObjectId(),
409  $ilUser->getId(),
410  false
411  );
412 
413  // ok, all done
414  $this->writelog('PROPFIND():true options=' . var_export($options, true) . ' files=' . var_export($files, true));
415  return true;
416  }
417 
428  private function isFileHidden(&$objDAV)
429  {
430  // Hide null resources which haven't got an active lock
431  if ($objDAV->isNullResource()) {
432  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
433  return;
434  }
435  }
436 
437  $name = $objDAV->getResourceName();
438  $isFileHidden = false;
439  switch ($this->clientOS) {
440  case 'unix':
441  // Hide Windows thumbnail files, and files which start with '~$'.
442  $isFileHidden =
443  $name == 'Thumbs.db'
444  || substr($name, 0, 2) == '~$';
445  // Hide files which contain /
446  $isFileHidden |= preg_match('/\\//', $name);
447  break;
448  case 'windows':
449  // Hide files that start with '.'.
450  $isFileHidden = substr($name, 0, 1) == '.';
451  // Hide files which contain \ / : * ? " < > |
452  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
453  break;
454  default:
455  // Hide files which contain /
456  $isFileHidden |= preg_match('/\\//', $name);
457  break;
458  }
459  $this->writelog($this->clientOS . ' ' . $name . ' isHidden:' . $isFileHidden . ' clientOS:' . $this->clientOS);
460  return $isFileHidden;
461  }
462 
470  private function fileinfo($resourcePath, $displayPath, &$objDAV)
471  {
472  global $ilias;
473 
474  $this->writelog('fileinfo(' . $resourcePath . ')');
475  // create result array
476  $info = array();
477  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
478  * end of a resource path to a collection.
479  * However Mac OS X does not like this!
480  */
481  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
482  $info['path'] = $resourcePath . '/';
483  } else {
484  $info['path'] = $resourcePath;
485  }
486 
487  $info['props'] = array();
488 
489  // no special beautified displayname here ...
490  $info["props"][] =&$this->mkprop("displayname", $displayPath);
491 
492  // creation and modification time
493  $info["props"][] =&$this->mkprop("creationdate", $objDAV->getCreationTimestamp());
494  $info["props"][] =&$this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
495 
496  // directory (WebDAV collection)
497  $info["props"][] =&$this->mkprop("resourcetype", $objDAV->getResourceType());
498  $info["props"][] =&$this->mkprop("getcontenttype", $objDAV->getContentType());
499  $info["props"][] =&$this->mkprop("getcontentlength", $objDAV->getContentLength());
500 
501  // Only show supported locks for users who have write permission
502  if ($objDAV->isPermitted('write')) {
503  $info["props"][] =&$this->mkprop(
504  "supportedlock",
505  '<D:lockentry>'
506  . '<D:lockscope><D:exclusive/></D:lockscope>'
507  . '<D:locktype><D:write/></D:locktype>'
508  . '</D:lockentry>'
509  . '<D:lockentry>'
510  . '<D:lockscope><D:shared/></D:lockscope>'
511  . '<D:locktype><D:write/></D:locktype>'
512  . '</D:lockentry>'
513  );
514  }
515 
516  // Maybe we should only show locks on objects for users who have write permission.
517  // But if we don't show these locks, users who have write permission in an object
518  // further down in a hierarchy can't see who is locking their object.
519  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
520  $lockdiscovery = '';
521  foreach ($locks as $lock) {
522  // DAV Clients expects to see their own owner name in
523  // the locks. Since these names are not unique (they may
524  // just be the name of the local user running the DAV client)
525  // we return the ILIAS user name in all other cases.
526  if ($lock['ilias_owner'] == $ilias->account->getId()) {
527  $owner = $lock['dav_owner'];
528  } else {
529  $owner = '<D:href>' . $this->getLogin($lock['ilias_owner']) . '</D:href>';
530  }
531  $this->writelog('lockowner=' . $owner . ' ibi:' . $lock['ilias_owner'] . ' davi:' . $lock['dav_owner']);
532 
533  $lockdiscovery .=
534  '<D:activelock>'
535  . '<D:lockscope><D:' . $lock['scope'] . '/></D:lockscope>'
536  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
537  . '<D:locktype><D:write/></D:locktype>'
538  . '<D:depth>' . $lock['depth'] . '</D:depth>'
539  . '<D:owner>' . $owner . '</D:owner>'
540 
541  // more than a million is considered an absolute timestamp
542  // less is more likely a relative value
543  . '<D:timeout>Second-' . (($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']) . '</D:timeout>'
544  . '<D:locktoken><D:href>' . $lock['token'] . '</D:href></D:locktoken>'
545  . '</D:activelock>'
546  ;
547  }
548  if (strlen($lockdiscovery) > 0) {
549  $info["props"][] =&$this->mkprop("lockdiscovery", $lockdiscovery);
550  }
551 
552  // get additional properties from database
553  $properties = $this->properties->getAll($objDAV);
554  foreach ($properties as $prop) {
555  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
556  }
557 
558  //$this->writelog('fileinfo():'.var_export($info, true));
559  return $info;
560  }
561 
573  public function GET(&$options)
574  {
575  global $ilUser;
576 
577  $this->writelog('GET(' . var_export($options, true) . ')');
578  $this->writelog('GET(' . $options['path'] . ')');
579 
580  // get dav object for path
581  $path = $this->davDeslashify($options['path']);
582  $objDAV =&$this->getObject($path);
583 
584  // sanity check
585  if (is_null($objDAV) || $objDAV->isNullResource()) {
586  return false;
587  }
588 
589  if (!$objDAV->isPermitted('visible,read')) {
590  return '403 Forbidden';
591  }
592 
593  // is this a collection?
594  if ($objDAV->isCollection()) {
595  if (isset($_GET['mount'])) {
596  return $this->mountDir($objDAV, $options);
597  } elseif (isset($_GET['mount-instructions'])) {
598  return $this->showMountInstructions($objDAV, $options);
599  } else {
600  return $this->getDir($objDAV, $options);
601  }
602  }
603  // detect content type
604  $options['mimetype'] =&$objDAV->getContentType();
605  // detect modification time
606  // see rfc2518, section 13.7
607  // some clients seem to treat this as a reverse rule
608  // requiring a Last-Modified header if the getlastmodified header was set
609  $options['mtime'] =&$objDAV->getModificationTimestamp();
610 
611  // detect content length
612  $options['size'] =&$objDAV->getContentLength();
613 
614  // get content as stream or as data array
615  $options['stream'] =&$objDAV->getContentStream();
616  if (is_null($options['stream'])) {
617  $options['data'] =&$objDAV->getContentData();
618  }
619 
620  // Record read event and catch up write events
622  $objDAV->getILIASType(),
623  $objDAV->getRefId(),
624  $objDAV->getObjectId(),
625  $ilUser->getId()
626  );
627 
628  $this->writelog('GET:' . var_export($options, true));
629 
630  return true;
631  }
643  private function mountDir(&$objDAV, &$options)
644  {
645  $path = $this->davDeslashify($options['path']);
646 
647  header('Content-Type: application/davmount+xml');
648 
649  echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
650  echo " </dm:url>" . $this->base_uri . "</dm:url>\n";
651 
652  $xmlPath = str_replace('&', '&amp;', $path);
653  $xmlPath = str_replace('<', '&lt;', $xmlPath);
654  $xmlPath = str_replace('>', '&gt;', $xmlPath);
655 
656  echo " </dm:open>$xmlPath</dm:open>\n";
657  echo "</dm:mount>\n";
658 
659  exit;
660  }
667  private function showMountInstructions(&$objDAV, &$options)
668  {
669  global $lng,$ilUser;
670 
671  $path = $this->davDeslashify($options['path']);
672 
673  // The $path variable may contain a full or a shortened DAV path.
674  // We convert it into an object path, which we can then use to
675  // construct a new full DAV path.
676  $objectPath = $this->toObjectPath($path);
677 
678  // Construct a (possibly) full DAV path from the object path.
679  $fullPath = '';
680  foreach ($objectPath as $object) {
681  if ($object->getRefId() == 1 && $this->isFileHidden($object)) {
682  // If the repository root object is hidden, we can not
683  // create a full path, because nothing would appear in the
684  // webfolder. We resort to a shortened path instead.
685  $fullPath .= '/ref_1';
686  } else {
687  $fullPath .= '/' . $this->davUrlEncode($object->getResourceName());
688  }
689  }
690 
691  // Construct a shortened DAV path from the object path.
692  $shortenedPath = '/ref_' .
693  $objectPath[count($objectPath) - 1]->getRefId();
694 
695  if ($objDAV->isCollection()) {
696  $shortenedPath .= '/';
697  $fullPath .= '/';
698  }
699 
700  // Prepend client id to path
701  $shortenedPath = '/' . CLIENT_ID . $shortenedPath;
702  $fullPath = '/' . CLIENT_ID . $fullPath;
703 
704  // Construct webfolder URI's. The URI's are used for mounting the
705  // webfolder. Since mounting using URI's is not standardized, we have
706  // to create different URI's for different browsers.
707  $webfolderURI = $this->base_uri . $shortenedPath;
708  $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav") .
709  substr($this->base_uri, strrpos($this->base_uri, ':')) .
710  $shortenedPath;
711  ;
712  $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav") .
713  substr($this->base_uri, strrpos($this->base_uri, ':')) .
714  $shortenedPath
715  ;
716  $webfolderURI_IE = $this->base_uri . $shortenedPath;
717 
718  $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
719 
720  header('Content-Type: text/html; charset=UTF-8');
721  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
722  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
723  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
724  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
725  echo " <head>\n";
726  echo " <title>" . sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle) . "</title>\n";
727  echo " </head>\n";
728  echo " <body>\n";
729 
731  $webfolderTitle,
732  $webfolderURI,
733  $webfolderURI_IE,
734  $webfolderURI_Konqueror,
735  $webfolderURI_Nautilus,
736  $this->clientOS,
737  $this->clientOSFlavor
738  );
739 
740  echo " </body>\n";
741  echo "</html>\n";
742 
743  // Logout anonymous user to force authentication after calling mount uri
744  if ($ilUser->getId() == ANONYMOUS_USER_ID) {
745  $GOBALS['DIC']['ilAuthSession']->logout();
746  }
747 
748  exit;
749  }
759  private function getDir(&$objDAV, &$options)
760  {
761  global $ilias, $lng;
762 
763  // Activate tree cache
764  global $tree;
765  //$tree->useCache(true);
766 
767  $path = $this->davDeslashify($options['path']);
768 
769  // The URL of a directory must end with a slash.
770  // If it does not we are redirecting the browser.
771  // The slash is required, because we are using relative links in the
772  // HTML code we are generating below.
773  if ($path . '/' != $options['path']) {
774  header('Location: ' . $this->base_uri . $path . '/');
775  exit;
776  }
777 
778  header('Content-Type: text/html; charset=UTF-8');
779 
780  // fixed width directory column format
781  $format = "%15s %-19s %-s\n";
782 
783  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
784  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
785  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
786  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
787  echo "<head>\n";
788  echo "<title>" . sprintf($lng->txt('webfolder_index_of'), $path) . "</title>\n";
789 
790  // Create "anchorClick" behavior for for Internet Explorer
791  // This allows to create a link to a webfolder
792  echo "<style type=\"text/css\">\n";
793  echo "<!--\n";
794  echo "a {\n";
795  echo " behavior:url(#default#AnchorClick);\n";
796  echo "}\n";
797  echo "-->\n";
798  echo "</style>\n";
799 
800  echo "</head><body>\n";
801 
802  $hrefPath = '';
803  $pathComponents = explode('/', $path);
804  $uriComponents = array();
805  foreach ($pathComponents as $component) {
806  $uriComponents[] = $this->davUrlEncode($component);
807  }
808  for ($i = 0; $i < count($pathComponents); $i++) {
809  $displayName = htmlspecialchars($pathComponents[$i]);
810  if ($i != 0) {
811  $hrefPath .= '/';
812  }
813  $uriPath = implode('/', array_slice($uriComponents, 0, $i + 1));
814  if ($i < 2) {
815  // The first two path elements consist of the webdav.php script
816  // and the client id. These elements are not part of the
817  // directory structure and thus are not represented as links.
818  $hrefPath .= $displayName;
819  } else {
820  $hrefPath .= '<a href="' . $this->base_uri . $uriPath . '/">' . $displayName . '</a>';
821  }
822  }
823  echo "<h3>" . sprintf($lng->txt('webfolder_index_of'), $hrefPath) . "</h3>\n";
824 
825  // Display user id
826  if ($ilias->account->getLogin() == 'anonymous') {
827  echo "<p><font size=\"-1\">" . $lng->txt('not_logged_in') . "</font><br>\n";
828  } else {
829  echo "<p><font size=\"-1\">" . $lng->txt('login_as') . " <i>"
830  . $ilias->account->getFirstname() . ' '
831  . $ilias->account->getLastname() . ' '
832  . ' ' . $ilias->account->getLogin() . '</i> '
833  . ', ' . $lng->txt('client') . ' <i>' . $ilias->getClientId() . '</i>.'
834  . "</font><p>\n";
835  }
836 
837  // Create "open as webfolder" link
838  $href = $this->base_uri . $uriPath;
839  // IE can not mount long paths. If the path has more than one element, we
840  // create a relative path with a ref-id.
841  if (count($pathComponents) > 2) {
842  $hrefIE = $this->base_uri . '/' . CLIENT_ID . '/ref_' . $objDAV->getRefId();
843  } else {
844  $hrefIE = $href;
845  }
846  echo "<p><font size=\"-1\">" .
847  sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions") .
848  "</font></p>\n";
849  echo "<p><font size=\"-1\">" .
850  sprintf(
851  $lng->txt('webfolder_mount_dir_with'),
852  "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
853  'webdav' . substr($href, 4), // Konqueror
854  'dav' . substr($href, 4), // Nautilus
855  $href . '?mount' // RFC 4709
856  )
857  . "</font></p>\n";
858 
859  echo "<pre>";
860  printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
861  echo "<hr>";
862 
863  $collectionCount = 0;
864  $fileCount = 0;
865  $children =&$objDAV->childrenWithPermission('visible,read');
866  foreach ($children as $childDAV) {
867  if ($childDAV->isCollection() && !$this->isFileHidden($childDAV)) {
868  $collectionCount++;
869  $name = $this->davUrlEncode($childDAV->getResourceName());
870  printf(
871  $format,
872  '-',
873  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
874  '<a href="' . $name . '/' . '">' . $childDAV->getDisplayName() . "</a>"
875  );
876  }
877  }
878  foreach ($children as $childDAV) {
879  if ($childDAV->isFile() && !$this->isFileHidden($childDAV)) {
880  $fileCount++;
881  $name = $this->davUrlEncode($childDAV->getResourceName());
882  printf(
883  $format,
884  number_format($childDAV->getContentLength()),
885  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
886  '<a href="' . $name . '">' . $childDAV->getDisplayName() . "</a>"
887  );
888  }
889  }
890  foreach ($children as $childDAV) {
891  if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV)) {
892  $name = $this->davUrlEncode($childDAV->getResourceName());
893  printf(
894  $format,
895  'Lock',
896  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
897  '<a href="' . $name . '">' . $childDAV->getDisplayName() . "</a>"
898  );
899  }
900  }
901  echo "<hr>";
902  echo $collectionCount . ' ' . $lng->txt(($collectionCount == 1) ? 'folder' : 'folders') . ', ';
903  echo $fileCount . ' ' . $lng->txt(($fileCount == 1) ? 'file' : 'files') . '.';
904  echo "</pre>";
905  echo "</body></html>\n";
906 
907  exit;
908  }
909 
910 
917  public function PUT(&$options)
918  {
919  global $ilUser;
920 
921  $this->writelog('PUT(' . var_export($options, true) . ')');
922 
923  $path = $this->davDeslashify($options['path']);
924  $parent = dirname($path);
925  $name = $this->davBasename($path);
926 
927  //Check if FileType is allowed
929  return false;
930  }
931 
932  // get dav object for path
933  $parentDAV =&$this->getObject($parent);
934 
935  // sanity check
936  if (is_null($parentDAV) || !$parentDAV->isCollection()) {
937  return '409 Conflict';
938  }
939 
940  // Prevent putting of files which exceed upload limit
941  // FIXME: since this is an optional parameter, we should to do the
942  // same check again in function PUTfinished.
943  if ($options['content_length'] != null &&
944  $options['content_length'] > $this->getUploadMaxFilesize()) {
945  $this->writelog('PUT is forbidden, because content length=' .
946  $options['content_length'] . ' is larger than upload_max_filesize=' .
947  $this->getUploadMaxFilesize() . 'in php.ini');
948 
949  return '403 Forbidden';
950  }
951 
952  // determine mime type
953  include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
955 
956  $objDAV =&$this->getObject($path);
957  if (is_null($objDAV)) {
958  $ttype = $parentDAV->getILIASFileType();
959  $isperm = $parentDAV->isPermitted('create', $ttype);
960  if (!$isperm) {
961  $this->writelog('PUT is forbidden, because user has no create permission');
962 
963  return '403 Forbidden';
964  }
965  $options["new"] = true;
966  $objDAV =&$parentDAV->createFile($name);
967  $this->writelog('PUT obj=' . $objDAV . ' name=' . $name . ' content_type=' . $options['content_type']);
968  //$objDAV->setContentType($options['content_type']);
969  $objDAV->setContentType($mime);
970  if ($options['content_length'] != null) {
971  $objDAV->setContentLength($options['content_length']);
972  }
973  $objDAV->write();
974  // Record write event
975  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
976  } elseif ($objDAV->isNullResource()) {
977  if (!$parentDAV->isPermitted('create', $parentDAV->getILIASFileType())) {
978  $this->writelog('PUT is forbidden, because user has no create permission');
979  return '403 Forbidden';
980  }
981  $options["new"] = false;
982  $objDAV =&$parentDAV->createFileFromNull($name, $objDAV);
983  $this->writelog('PUT obj=' . $objDAV . ' name=' . $name . ' content_type=' . $options['content_type']);
984  //$objDAV->setContentType($options['content_type']);
985  $objDAV->setContentType($mime);
986  if ($options['content_length'] != null) {
987  $objDAV->setContentLength($options['content_length']);
988  }
989  $objDAV->write();
990 
991  // Record write event
992  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
993  } else {
994  if (!$objDAV->isPermitted('write')) {
995  $this->writelog('PUT is forbidden, because user has no write permission');
996  return '403 Forbidden';
997  }
998  $options["new"] = false;
999  $this->writelog('PUT obj=' . $objDAV . ' name=' . $name . ' content_type=' . $options['content_type'] . ' content_length=' . $options['content_length']);
1000 
1001  // Create a new version if the previous version is not empty
1002  if ($objDAV->getContentLength() != 0) {
1003  $objDAV->createNewVersion();
1004  }
1005 
1006  //$objDAV->setContentType($options['content_type']);
1007  $objDAV->setContentType($mime);
1008  if ($options['content_length'] != null) {
1009  $objDAV->setContentLength($options['content_length']);
1010  }
1011  $objDAV->write();
1012 
1013  // Record write event
1014  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
1015  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
1016  }
1017  // store this object, we reuse it in method PUTfinished
1018  $this->putObjDAV = $objDAV;
1019 
1020  $out =&$objDAV->getContentOutputStream();
1021  $this->writelog('PUT outputstream=' . $out);
1022 
1023  return $out;
1024  }
1025 
1032  public function PUTfinished(&$options)
1033  {
1034  $this->writelog('PUTfinished(' . var_export($options, true) . ')');
1035 
1036  if ($this->putObjDAV->getResourceType()=="") {
1037  $vir = ilUtil::virusHandling($this->putObjDAV->obj->getDirectory($this->putObjDAV->obj->getVersion()) . '/' . $this->putObjDAV->obj->getFileName(), $this->putObjDAV->obj->getFileName());
1038  if ($vir[0] == false) {
1039  $this->writelog('PUTfinished Virus found: ' . $vir[1]);
1040  //delete file
1042  return false;
1043  }
1044  }
1045 
1046  // Update the content length in the file object, if the
1047  // the client did not specify a content_length
1048  if ($options['content_length'] == null || $this->putObjDAV->getContentLength() == 0) {
1049  $objDAV = $this->putObjDAV;
1050  if ($objDAV->getContentOutputStreamLength() != null) {
1051  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1052  } else {
1053  $objDAV->write();
1054  $objDAV->setContentLength(filesize($objDAV->obj->getDirectory($objDAV->obj->getVersion()) . '/' . $objDAV->obj->getFileName()));
1055  }
1056  $objDAV->write();
1057  $this->putObjDAV = null;
1058  }
1059  return true;
1060  }
1061 
1062 
1069  public function MKCOL($options)
1070  {
1071  global $ilUser;
1072 
1073  $this->writelog('MKCOL(' . var_export($options, true) . ')');
1074  $this->writelog('MKCOL ' . $options['path']);
1075 
1076  $path =&$this->davDeslashify($options['path']);
1077  $parent =&dirname($path);
1078  $name =&$this->davBasename($path);
1079 
1080  // No body parsing yet
1081  if (!empty($_SERVER["CONTENT_LENGTH"])) {
1082  return "415 Unsupported media type";
1083  }
1084 
1085  // Check if an object with the path already exists.
1086  $objDAV =&$this->getObject($path);
1087  if (!is_null($objDAV)) {
1088  return '405 Method not allowed';
1089  }
1090 
1091  // get parent dav object for path
1092  $parentDAV =&$this->getObject($parent);
1093 
1094  // sanity check
1095  if (is_null($parentDAV) || !$parentDAV->isCollection()) {
1096  return '409 Conflict';
1097  }
1098 
1099  if (!$parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType())) {
1100  return '403 Forbidden';
1101  }
1102 
1103  // XXX Implement code that Handles null resource here
1104 
1105  $objDAV = $parentDAV->createCollection($name);
1106 
1107  if ($objDAV != null) {
1108  // Record write event
1109  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1110  }
1111 
1112  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1113  return $result;
1114  }
1115 
1116 
1123  public function DELETE($options)
1124  {
1125  global $ilUser;
1126 
1127  $this->writelog('DELETE(' . var_export($options, true) . ')');
1128  $this->writelog('DELETE ' . $options['path']);
1129 
1130  // get dav object for path
1131  $path =&$this->davDeslashify($options['path']);
1132  $parentDAV =&$this->getObject(dirname($path));
1133  $objDAV =&$this->getObject($path);
1134 
1135  // sanity check
1136  if (is_null($objDAV) || $objDAV->isNullResource()) {
1137  return '404 Not Found';
1138  }
1139  if (!$objDAV->isPermitted('delete')) {
1140  return '403 Forbidden';
1141  }
1142 
1143  $parentDAV->remove($objDAV);
1144 
1145  // Record write event
1146  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1147 
1148  return '204 No Content';
1149  }
1150 
1157  public function MOVE($options)
1158  {
1159  global $ilUser;
1160 
1161  $this->writelog('MOVE(' . var_export($options, true) . ')');
1162  $this->writelog('MOVE ' . $options['path'] . ' ' . $options['dest']);
1163 
1164  // Get path names
1165  $src = $this->davDeslashify($options['path']);
1166  $srcParent = dirname($src);
1167  $srcName = $this->davBasename($src);
1168  $dst = $this->davDeslashify($options['dest']);
1169 
1170  $dstParent = dirname($dst);
1171  $dstName = $this->davBasename($dst);
1172  $this->writelog('move ' . $dst . ' dstname=' . $dstName);
1173  // Source and destination must not be the same
1174  if ($src == $dst) {
1175  return '409 Conflict (source and destination are the same)';
1176  }
1177 
1178  // Destination must not be in a subtree of source
1179  if (substr($dst, strlen($src)+1) == $src . '/') {
1180  return '409 Conflict (destination is in subtree of source)';
1181  }
1182 
1183  // Get dav objects for path
1184  $srcDAV =&$this->getObject($src);
1185  $dstDAV =&$this->getObject($dst);
1186  $srcParentDAV =&$this->getObject($srcParent);
1187  $dstParentDAV =&$this->getObject($dstParent);
1188 
1189  // Source must exist
1190  if ($srcDAV == null) {
1191  return '409 Conflict (source does not exist)';
1192  }
1193 
1194  // Overwriting is only allowed, if overwrite option is set to 'T'
1195  $isOverwritten = false;
1196  if ($dstDAV != null) {
1197  if ($options['overwrite'] == 'T') {
1198  // Delete the overwritten destination
1199  if ($dstDAV->isPermitted('delete')) {
1200  $dstParentDAV->remove($dstDAV);
1201  $dstDAV = null;
1202  $isOverwritten = true;
1203  } else {
1204  return '403 Not Permitted';
1205  }
1206  } else {
1207  return '412 Precondition Failed';
1208  }
1209  }
1210 
1211  // Parents of destination must exist
1212  if ($dstParentDAV == null) {
1213  return '409 Conflict (parent of destination does not exist)';
1214  }
1215 
1216  if ($srcParent == $dstParent) {
1217  // Rename source, if source and dest are in same parent
1218 
1219  // Check permission
1220  if (!$srcDAV->isPermitted('write')) {
1221  return '403 Forbidden';
1222  }
1223  $this->writelog('rename dstName=' . $dstName);
1224  $srcDAV->setResourceName($dstName);
1225  $srcDAV->write();
1226  } else {
1227  // Move source, if source and dest are in same parent
1228 
1229 
1230  if (!$srcDAV->isPermitted('delete')) {
1231  return '403 Forbidden';
1232  }
1233 
1234  if (!$dstParentDAV->isPermitted('create', $srcDAV->getILIASType())) {
1235  return '403 Forbidden';
1236  }
1237  $dstParentDAV->addMove($srcDAV, $dstName);
1238  }
1239 
1240  // Record write event
1241  if ($isOverwritten) {
1242  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1243  } else {
1244  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1245  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1246  }
1247 
1248  return ($isOverwritten) ? '204 No Content' : '201 Created';
1249  }
1250 
1257  public function COPY($options, $del=false)
1258  {
1259  global $ilUser;
1260  $this->writelog('COPY(' . var_export($options, true) . ' ,del=' . $del . ')');
1261  $this->writelog('COPY ' . $options['path'] . ' ' . $options['dest']);
1262 
1263  // no copying to different WebDAV Servers
1264  if (isset($options["dest_url"])) {
1265  return "502 bad gateway";
1266  }
1267 
1268  $src = $this->davDeslashify($options['path']);
1269  $srcParent = dirname($src);
1270  $srcName = $this->davBasename($src);
1271  $dst = $this->davDeslashify($options['dest']);
1272  $dstParent = dirname($dst);
1273  $dstName = $this->davBasename($dst);
1274 
1275  // sanity check
1276  if ($src == $dst) {
1277  return '409 Conflict'; // src and dst are the same
1278  }
1279 
1280  if (substr($dst, strlen($src)+1) == $src . '/') {
1281  return '409 Conflict'; // dst is in subtree of src
1282  }
1283 
1284  $this->writelog('COPY src=' . $src . ' dst=' . $dst);
1285  // get dav object for path
1286  $srcDAV =&$this->getObject($src);
1287  $dstDAV =&$this->getObject($dst);
1288  $dstParentDAV =&$this->getObject($dstParent);
1289 
1290  if (is_null($srcDAV) || $srcDAV->isNullResource()) {
1291  return '409 Conflict'; // src does not exist
1292  }
1293  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource()) {
1294  return '409 Conflict'; // parent of dst does not exist
1295  }
1296  $isOverwritten = false;
1297 
1298  // XXX Handle nulltype for dstDAV
1299  if (!is_null($dstDAV)) {
1300  if ($options['overwrite'] == 'T') {
1301  if ($dstDAV->isPermitted('delete')) {
1302  $dstParentDAV->remove($dstDAV);
1303  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1304 
1305  $dstDAV = null;
1306  $isOverwritten = true;
1307  } else {
1308  return '403 Forbidden';
1309  }
1310  } else {
1311  return '412 Precondition Failed';
1312  }
1313  }
1314 
1315  if (!$dstParentDAV->isPermitted('create', $srcDAV->getILIASType())) {
1316  return '403 Forbidden';
1317  }
1318  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1319 
1320  // Record write event
1322  $srcDAV->getILIASType(),
1323  $srcDAV->getRefId(),
1324  $srcDAV->getObjectId(),
1325  $ilUser->getId()
1326  );
1327  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1328 
1329  return ($isOverwritten) ? '204 No Content' : '201 Created';
1330  }
1331 
1338  public function PROPPATCH(&$options)
1339  {
1340  $this->writelog('PROPPATCH(options=' . var_export($options, true) . ')');
1341  $this->writelog('PROPPATCH ' . $options['path']);
1342 
1343  // get dav object for path
1344  $path =&$this->davDeslashify($options['path']);
1345  $objDAV =&$this->getObject($path);
1346 
1347  // sanity check
1348  if (is_null($objDAV) || $objDAV->isNullResource()) {
1349  return false;
1350  }
1351 
1352  $isPermitted = $objDAV->isPermitted('write');
1353  foreach ($options['props'] as $key => $prop) {
1354  if (!$isPermitted || $prop['ns'] == 'DAV:') {
1355  $options['props'][$key]['status'] = '403 Forbidden';
1356  } else {
1357  $this->properties->put($objDAV, $prop['ns'], $prop['name'], $prop['val']);
1358  }
1359  }
1360 
1361  return "";
1362  }
1363 
1364 
1371  public function LOCK(&$options)
1372  {
1373  global $ilias;
1374  $this->writelog('LOCK(' . var_export($options, true) . ')');
1375  $this->writelog('LOCK ' . $options['path']);
1376 
1377  // Check if an object with the path exists.
1378  $path =&$this->davDeslashify($options['path']);
1379  $objDAV =&$this->getObject($path);
1380  // Handle null-object locking
1381  // --------------------------
1382  if (is_null($objDAV)) {
1383  $this->writelog('LOCK handling null-object locking...');
1384 
1385  // If the name does not exist, we create a null-object for it
1386  if (isset($options["update"])) {
1387  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1388  return '412 Precondition Failed';
1389  }
1390 
1391  $parent = dirname($path);
1392  $parentDAV =&$this->getObject($parent);
1393  if (is_null($parentDAV)) {
1394  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1395  return '404 Not Found';
1396  }
1397  if (!$parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1398  !$parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType())) {
1399  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1400  return '403 Forbidden';
1401  }
1402 
1403  $objDAV =&$parentDAV->createNull($this->davBasename($path));
1404  $this->writelog('created null resource for ' . $path);
1405  }
1406 
1407  // ---------------------
1408  if (!$objDAV->isNullResource() && !$objDAV->isPermitted('write')) {
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  $options["timeout"] = time()+360; // 6min.
1416  }
1417 
1418  if (isset($options["update"])) { // Lock Update
1419  $this->writelog('LOCK update token=' . var_export($options, true));
1420  $success = $this->locks->updateLockWithoutCheckingDAV(
1421  $objDAV,
1422  $options['update'],
1423  $options['timeout']
1424  );
1425  if ($success) {
1426  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1427  if ($data['ilias_owner'] == $ilias->account->getId()) {
1428  $owner = $data['dav_owner'];
1429  } else {
1430  $owner = '<D:href>' . $this->getLogin($data['ilias_owner']) . '</D:href>';
1431  }
1432  $options['owner'] = $owner;
1433  $options['locktoken'] = $data['token'];
1434  $options['timeout'] = $data['expires'];
1435  $options['depth'] = $data['depth'];
1436  $options['scope'] = $data['scope'];
1437  $options['type'] = $data['scope'];
1438  }
1439  } else {
1440  $this->writelog('LOCK create new lock');
1441 
1442  // XXX - Attempting to create a recursive exclusive lock
1443  // on a collection must fail, if any of nodes in the subtree
1444  // of the collection already has a lock.
1445  // XXX - Attempting to create a recursive shared lock
1446  // on a collection must fail, if any of nodes in the subtree
1447  // of the collection already has an exclusive lock.
1448  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1449  $this->writelog('lock owner=' . $owner);
1450  $success = $this->locks->lockWithoutCheckingDAV(
1451  $objDAV,
1452  $ilias->account->getId(),
1453  trim($options['owner']),
1454  $options['locktoken'],
1455  $options['timeout'],
1456  $options['depth'],
1457  $options['scope']
1458  );
1459  }
1460 
1461  // Note: As a workaround for the Microsoft WebDAV Client, we return
1462  // true/false here (resulting in the status '200 OK') instead of
1463  // '204 No Content').
1464  //return ($success) ? '204 No Content' : false;
1465  return $success;
1466  }
1467 
1474  public function UNLOCK(&$options)
1475  {
1476  global $log, $ilias;
1477  $this->writelog('UNLOCK(options=' . var_export($options, true) . ')');
1478  $this->writelog('UNLOCK ' . $options['path']);
1479 
1480  // Check if an object with the path exists.
1481  $path =&$this->davDeslashify($options['path']);
1482  $objDAV =&$this->getObject($path);
1483  if (is_null($objDAV)) {
1484  return '404 Not Found';
1485  }
1486  if (!$objDAV->isPermitted('write')) {
1487  return '403 Forbidden';
1488  }
1489 
1490  $success = $this->locks->unlockWithoutCheckingDAV(
1491  $objDAV,
1492  $options['token']
1493  );
1494 
1495  // Delete null resource object if there are no locks associated to
1496  // it anymore
1497  if ($objDAV->isNullResource()
1498  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
1499  $parent = dirname($this->davDeslashify($options['path']));
1500  $parentDAV =&$this->getObject($parent);
1501  $parentDAV->remove($objDAV);
1502  }
1503 
1504  // Workaround for Mac OS X: We must return 200 here instead of
1505  // 204.
1506  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1507  return ($success) ? '200 OK' : '412 Precondition Failed';
1508  }
1509 
1522  protected function checkLock($path)
1523  {
1524  global $ilias;
1525 
1526  $this->writelog('checkLock(' . $path . ')');
1527  $result = null;
1528 
1529  // get dav object for path
1530  //$objDAV = $this->getObject($path);
1531 
1532  // convert DAV path into ilObjectDAV path
1533  $objPath = $this->toObjectPath($path);
1534  if (!is_null($objPath)) {
1535  $objDAV = $objPath[count($objPath) - 1];
1536  $locks = $this->locks->getLocksOnPathDAV($objPath);
1537  foreach ($locks as $lock) {
1538  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1539  && $lock['node_id'] == $objDAV->getNodeId();
1540 
1541  // Check all locks on last object in path,
1542  // but only locks with depth infinity on parent objects.
1543  if ($isLastPathComponent || $lock['depth'] == 'infinity') {
1544  // DAV Clients expects to see their own owner name in
1545  // the locks. Since these names are not unique (they may
1546  // just be the name of the local user running the DAV client)
1547  // we return the ILIAS user name in all other cases.
1548  if ($lock['ilias_owner'] == $ilias->account->getId()) {
1549  $owner = $lock['dav_owner'];
1550  } else {
1551  $owner = $this->getLogin($lock['ilias_owner']);
1552  }
1553 
1554  // FIXME - Shouldn't we collect all locks instead of
1555  // using an arbitrary one?
1556  $result = array(
1557  "type" => "write",
1558  "obj_id" => $lock['obj_id'],
1559  "node_id" => $lock['node_id'],
1560  "scope" => $lock['scope'],
1561  "depth" => $lock['depth'],
1562  "owner" => $owner,
1563  "token" => $lock['token'],
1564  "expires" => $lock['expires']
1565  );
1566  if ($lock['scope'] == 'exclusive') {
1567  // If there is an exclusive lock in the path, it
1568  // takes precedence over all non-exclusive locks in
1569  // parent nodes. Therefore we can can finish collecting
1570  // locks.
1571  break;
1572  }
1573  }
1574  }
1575  }
1576  $this->writelog('checkLock(' . $path . '):' . var_export($result, true));
1577 
1578  return $result;
1579  }
1580 
1585  protected function getLogin($userId)
1586  {
1587  $login = ilObjUser::_lookupLogin($userId);
1588  $this->writelog('getLogin(' . $userId . '):' . var_export($login, true));
1589  return $login;
1590  }
1591 
1592 
1599  private function getObject($davPath)
1600  {
1601  global $tree;
1602 
1603  // If the second path elements starts with 'file_', the following
1604  // characters of the path element directly identify the ref_id of
1605  // a file object.
1606  $davPathComponents = explode('/', substr($davPath, 1));
1607 
1608  if (count($davPathComponents) > 1 &&
1609  substr($davPathComponents[1], 0, 5) == 'file_') {
1610  $ref_id = substr($davPathComponents[1], 5);
1611  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1612 
1613  // Poor IE needs this, in order to successfully display
1614  // PDF documents
1615  header('Pragma: private');
1616  } else {
1617  $nodePath = $this->toNodePath($davPath);
1618  if ($nodePath == null && count($davPathComponents) == 1) {
1619  return ilObjectDAV::createObject(-1, 'mountPoint');
1620  }
1621  }
1622  if (is_null($nodePath)) {
1623  return null;
1624  } else {
1625  $top = $nodePath[count($nodePath) - 1];
1626  return ilObjectDAV::createObject($top['child'], $top['type']);
1627  }
1628  }
1635  private function toObjectPath($davPath)
1636  {
1637  $this->writelog('toObjectPath(' . $davPath);
1638  global $tree;
1639 
1640  $nodePath = $this->toNodePath($davPath);
1641 
1642  if (is_null($nodePath)) {
1643  return null;
1644  } else {
1645  $objectPath = array();
1646  foreach ($nodePath as $node) {
1647  $pathElement = ilObjectDAV::createObject($node['child'], $node['type']);
1648  if (is_null($pathElement)) {
1649  break;
1650  }
1651  $objectPath[] = $pathElement;
1652  }
1653  return $objectPath;
1654  }
1655  }
1656 
1668  public function toNodePath($davPath)
1669  {
1670  global $tree;
1671  $this->writelog('toNodePath(' . $davPath . ')...');
1672 
1673  // Split the davPath into path titles
1674  $titlePath = explode('/', substr($davPath, 1));
1675 
1676  // Remove the client id from the beginning of the title path
1677  if (count($titlePath) > 0) {
1678  array_shift($titlePath);
1679  }
1680 
1681  // If the last path title is empty, remove it
1682  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '') {
1683  array_pop($titlePath);
1684  }
1685 
1686  // If the path is empty, return null
1687  if (count($titlePath) == 0) {
1688  $this->writelog('toNodePath(' . $davPath . '):null, because path is empty.');
1689  return null;
1690  }
1691 
1692  // If the path is an absolute path, ref_id is null.
1693  $ref_id = null;
1694 
1695  // If the path is a relative folder path, convert it into an absolute path
1696  if (count($titlePath) > 0 && substr($titlePath[0], 0, 4) == 'ref_') {
1697  $ref_id = substr($titlePath[0], 4);
1698  array_shift($titlePath);
1699  }
1700 
1701  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1702 
1703  $this->writelog('toNodePath():' . var_export($nodePath, true));
1704  return $nodePath;
1705  }
1706 
1713  private function davDeslashify($path)
1714  {
1716 
1717  if ($path[strlen($path)-1] == '/') {
1718  $path = substr($path, 0, strlen($path) - 1);
1719  }
1720  $this->writelog('davDeslashify:' . $path);
1721  return $path;
1722  }
1723 
1730  private function davBasename($path)
1731  {
1732  $components = explode('/', $path);
1733  return count($components) == 0 ? '' : $components[count($components) - 1];
1734  }
1735 
1742  protected function writelog($message)
1743  {
1744  // Only write log message, if we are in debug mode
1745  if ($this->isDebug) {
1746  global $ilLog, $ilias;
1747  if ($ilLog) {
1748  if ($message == '---') {
1749  $ilLog->write('');
1750  } else {
1751  $ilLog->write(
1752  $ilias->account->getLogin()
1753  . ' ' . $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT']
1754  . ' ilDAVServer.' . str_replace("\n", ";", $message)
1755  );
1756  }
1757  } else {
1758  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1759  fwrite($fh, date('Y-m-d H:i:s '));
1760  fwrite($fh, str_replace("\n", ";", $message));
1761  fwrite($fh, "\n\n");
1762  fclose($fh);
1763  }
1764  }
1765  }
1766 
1779  public function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1780  {
1781  if ($genericURI) {
1782  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1783  $query = null;
1784  } elseif ($this->clientOS == 'windows') {
1785  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1786  $query = 'mount-instructions';
1787  } elseif ($this->clientBrowser == 'konqueror') {
1788  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1789  $query = null;
1790  } elseif ($this->clientBrowser == 'nautilus') {
1791  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1792  $query = null;
1793  } else {
1794  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1795  $query = 'mount-instructions';
1796  }
1797  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1798  $baseUri = substr($baseUri, 0, strrpos($baseUri, '/')) . '/webdav.php/' . CLIENT_ID;
1799 
1800  $uri = $baseUri . '/ref_' . $refId . '/';
1801  if ($query != null) {
1802  $uri .= '?' . $query;
1803  }
1804 
1805  return $uri;
1806  }
1821  public function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1822  {
1823  if ($this->clientOS == 'windows') {
1824  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1825  $query = null;
1826  } elseif ($this->clientBrowser == 'konqueror') {
1827  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1828  $query = null;
1829  } elseif ($this->clientBrowser == 'nautilus') {
1830  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1831  $query = null;
1832  } else {
1833  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1834  $query = null;
1835  }
1836  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1837  $baseUri = substr($baseUri, 0, strrpos($baseUri, '/')) . '/webdav.php/' . CLIENT_ID;
1838 
1839  $uri = $baseUri . '/ref_' . $refId . '/';
1840  if ($query != null) {
1841  $uri .= '?' . $query;
1842  }
1843 
1844  return $uri;
1845  }
1857  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1858  {
1859  $nodeId = 0;
1860  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:") .
1861  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1862  $baseUri = substr($baseUri, 0, strrpos($baseUri, '/')) . '/webdav.php/' . CLIENT_ID;
1863 
1864  if (!is_null($ressourceName) && !is_null($parentRefId)) {
1865  // Quickly create URI from the known data without needing SQL queries
1866  $uri = $baseUri . '/ref_' . $parentRefId . '/' . $this->davUrlEncode($ressourceName);
1867  } else {
1868  // Create URI and use some SQL queries to get the missing data
1869  global $tree;
1870  $nodePath = $tree->getNodePath($refId);
1871 
1872  if (is_null($nodePath) || count($nodePath) < 2) {
1873  // No object path? Return null - file is not in repository.
1874  $uri = null;
1875  } else {
1876  $uri = $baseUri . '/ref_' . $nodePath[count($nodePath) - 2]['child'] . '/' .
1877  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1878  }
1879  }
1880  return $uri;
1881  }
1882 
1900  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1901  {
1902  $nodeId = 0;
1903  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:") .
1904  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1905  $baseUri = substr($baseUri, 0, strrpos($baseUri, '/')) . '/webdav.php/' . CLIENT_ID;
1906 
1907  if (!is_null($ressourceName) && !is_null($parentRefId)) {
1908  // Quickly create URI from the known data without needing SQL queries
1909  $uri = $baseUri . '/file_' . $refId . '/' . $this->davUrlEncode($ressourceName);
1910  } else {
1911  // Create URI and use some SQL queries to get the missing data
1912  global $tree;
1913  $nodePath = $tree->getNodePath($refId);
1914 
1915  if (is_null($nodePath) || count($nodePath) < 2) {
1916  // No object path? Return null - file is not in repository.
1917  $uri = null;
1918  } else {
1919  $uri = $baseUri . '/file_' . $nodePath[count($nodePath) - 1]['child'] . '/' .
1920  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1921  }
1922  }
1923  return $uri;
1924  }
1925 
1931  public function isWebDAVoverHTTPS()
1932  {
1933  if ($this->isHTTPS == null) {
1934  global $ilSetting;
1935  require_once './Services/Http/classes/class.ilHTTPS.php';
1936  $https = new ilHTTPS();
1937  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1938  }
1939  return $this->isHTTPS;
1940  }
1941 
1950  public static function _isActive()
1951  {
1952  global $ilClientIniFile;
1953  return $ilClientIniFile->readVariable('file_access', 'webdav_enabled') == '1';
1954  }
1960  public static function _isActionsVisible()
1961  {
1962  global $ilClientIniFile;
1963  return $ilClientIniFile->readVariable('file_access', 'webdav_actions_visible') == '1';
1964  }
1965 
1975  public static function _getDefaultWebfolderInstructions()
1976  {
1977  global $lng;
1978  return $lng->txt('webfolder_instructions_text');
1979  }
1980 
2007  public static function _getWebfolderInstructionsFor(
2008  $webfolderTitle,
2009  $webfolderURI,
2010  $webfolderURI_IE,
2011  $webfolderURI_Konqueror,
2012  $webfolderURI_Nautilus,
2013  $os = 'unknown',
2014  $osFlavor = 'unknown'
2015  ) {
2016  global $ilSetting;
2017 
2018  $settings = new ilSetting('file_access');
2019  $str = $settings->get('custom_webfolder_instructions', '');
2020  if (strlen($str) == 0 || !$settings->get('custom_webfolder_instructions_enabled')) {
2022  }
2023  if (is_file('Customizing/clients/' . CLIENT_ID . '/webdavtemplate.htm')) {
2024  $str = fread(fopen('Customizing/clients/' . CLIENT_ID . '/webdavtemplate.htm', "rb"), filesize('Customizing/clients/' . CLIENT_ID . '/webdavtemplate.htm'));
2025  }
2026  $str=utf8_encode($str);
2027 
2028  preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2029  $refID=end($matches[0]);
2030 
2031  $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2032  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2033  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2034  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2035  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2036  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2037  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2038 
2039  if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')!==false) {
2040  $str = preg_replace('/\[IF_IEXPLORE\](?:(.*))\[\/IF_IEXPLORE\]/s', '\1', $str);
2041  } else {
2042  $str = preg_replace('/\[IF_NOTIEXPLORE\](?:(.*))\[\/IF_NOTIEXPLORE\]/s', '\1', $str);
2043  }
2044 
2045  switch ($os) {
2046  case 'windows':
2047  $operatingSystem = 'WINDOWS';
2048  break;
2049  case 'unix':
2050  switch ($osFlavor) {
2051  case 'osx':
2052  $operatingSystem = 'MAC';
2053  break;
2054  case 'linux':
2055  $operatingSystem = 'LINUX';
2056  break;
2057  default:
2058  $operatingSystem = 'LINUX';
2059  break;
2060  }
2061  break;
2062  default:
2063  $operatingSystem = 'UNKNOWN';
2064  break;
2065  }
2066 
2067  if ($operatingSystem != 'UNKNOWN') {
2068  $str = preg_replace('/\[IF_' . $operatingSystem . '\](?:(.*))\[\/IF_' . $operatingSystem . '\]/s', '\1', $str);
2069  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(.*))\[\/IF_\1\]/s', '', $str);
2070  } else {
2071  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(.*))\[\/IF_\1\]/s', '\2', $str);
2072  }
2073  return $str;
2074  }
2075 
2081  private function getUploadMaxFilesize()
2082  {
2083  $val = ini_get('upload_max_filesize');
2084 
2085  $val = trim($val);
2086  $last = strtolower($val[strlen($val)-1]);
2087  switch ($last) {
2088  // The 'G' modifier is available since PHP 5.1.0
2089  case 'g':
2090  $val *= 1024;
2091  // no break
2092  case 'm':
2093  $val *= 1024;
2094  // no break
2095  case 'k':
2096  $val *= 1024;
2097  }
2098 
2099  return $val;
2100  }
2101 }
2102 // END WebDAV
$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.
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.
$clientBrowser
The name of some well known browsers, that need special support.
$dst
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$dstName
Definition: consentform.php:53
$format
Definition: metadata.php:141
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
$result
$_GET["client_id"]
$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.
$end
Definition: saml1-acs.php:18
$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 lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=null)
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:1949
checkLock($path)
checkLock() helper
GET(&$options)
GET method handler.
static toNFC($string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:157
PUTfinished(&$options)
PUTfinished handler.
$clientOSFlavor
The flavor of the operating system of the WebDAV client.
if($format !==null) $name
Definition: metadata.php:146
serveRequest()
Serves a WebDAV request.
HTTPS.
$cachedObjectDAV
Cached object handler.
catch(Exception $e) $message
$clientOS
The operating system of the WebDAV client.
$success
Definition: Utf8Test.php:86
writelog($message)
Writes a message to the logfile.,.
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:1571
$locks
Handler for locks.
static _getWebfolderInstructionsFor( $webfolderTitle, $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus, $os='unknown', $osFlavor='unknown')
Gets Webfolder mount instructions for the specified webfolder.
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
$query
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.
if(is_array($dstName)) $srcName
Definition: consentform.php:52
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.
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
$i
Definition: disco.tpl.php:19
$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
$info
Definition: index.php:5
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 ...
$key
Definition: croninfo.php:18
const STATUS_ACCOUNT_MIGRATION_REQUIRED
static getValidFilename($a_filename)
Get valid filename.
if(!isset($_REQUEST['ReturnTo'])) if(!isset($_REQUEST['AuthId'])) $options
Definition: as_login.php:20
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.