ILIAS  Release_4_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilDAVServer.php
Go to the documentation of this file.
1 <?php
2 // BEGIN WebDAV
3 /*
4  +-----------------------------------------------------------------------------+
5  | ILIAS open source |
6  +-----------------------------------------------------------------------------+
7  | Copyright (c) 1998-2005 ILIAS open source, University of Cologne |
8  | |
9  | This program is free software; you can redistribute it and/or |
10  | modify it under the terms of the GNU General Public License |
11  | as published by the Free Software Foundation; either version 2 |
12  | of the License, or (at your option) any later version. |
13  | |
14  | This program is distributed in the hope that it will be useful, |
15  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17  | GNU General Public License for more details. |
18  | |
19  | You should have received a copy of the GNU General Public License |
20  | along with this program; if not, write to the Free Software |
21  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22  +-----------------------------------------------------------------------------+
23 */
24 
25 require_once "Services/WebDAV/classes/Server.php";
26 require_once "Services/WebDAV/classes/class.ilDAVLocks.php";
27 require_once "Services/WebDAV/classes/class.ilDAVProperties.php";
28 require_once 'Services/WebDAV/classes/class.ilObjectDAV.php';
29 
30 require_once "Services/User/classes/class.ilObjUser.php";
31 require_once('include/Unicode/UtfNormal.php');
32 require_once('Services/Tracking/classes/class.ilChangeEvent.php');
33 
51 {
57 
61  private $locks;
65  private $properties;
66 
72  private $clientOS = 'unknown';
77  private $clientOSFlavor = 'unknown';
82  private $clientBrowser = 'unknown';
83 
94  private $putObjDAV = null;
95 
102  private $isHTTPS = null;
103 
108  private $isDebug = false;
109 
115  public function ilDAVServer()
116  {
117  $this->writelog("<constructor>");
118 
119  // Initialize the WebDAV server and create
120  // locking and property support objects
121  $this->HTTP_WebDAV_Server();
122  $this->locks =& new ilDAVLocks();
123  $this->properties =& new ilDAVProperties();
124  //$this->locks->createTable();
125  //$this->properties->createTable();
126 
127 
128  // Guess operating system, operating system flavor and browser of the webdav client
129  //
130  // - We need to know the operating system in order to properly
131  // hide hidden resources in directory listings.
132  //
133  // - We need the operating system flavor and the browser to
134  // properly support mounting of a webdav folder.
135  //
136  $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
137  $this->writelog('userAgent='.$userAgent);
138  if (strpos($userAgent,'windows') !== false
139  || strpos($userAgent,'microsoft') !== false)
140  {
141  $this->clientOS = 'windows';
142  $this->clientOSFlavor = 'xp';
143 
144  } else if (strpos($userAgent,'darwin') !== false
145  || strpos($userAgent,'macintosh') !== false
146  || strpos($userAgent,'linux') !== false
147  || strpos($userAgent,'solaris') !== false
148  || strpos($userAgent,'aix') !== false
149  || strpos($userAgent,'unix') !== false
150  || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
151  )
152  {
153  $this->clientOS = 'unix';
154  if (strpos($userAgent,'linux') !== false)
155  {
156  $this->clientOSFlavor = 'linux';
157  }
158  else if (strpos($userAgent,'macintosh') !== false)
159  {
160  $this->clientOSFlavor = 'osx';
161  }
162  }
163  if (strpos($userAgent,'konqueror') !== false)
164  {
165  $this->clientBrowser = 'konqueror';
166  }
167  }
168 
172  public function serveRequest()
173  {
174  // die quickly if plugin is deactivated
175  if (!self::_isActive())
176  {
177  $this->http_status('403 Forbidden');
178  echo '<html><body><h1>Sorry</h1>'.
179  '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
180  '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
181  '</body></html>';
182  return;
183  }
184 
185  try {
186  $start = time();
187  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
189  $end = time();
190  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
191  $this->writelog('---');
192  } catch (Exception $e) {
193  $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
194  }
195  }
196 
231  private function davUrlEncode($path)
232  {
233  // We compose the path to Unicode Normal Form NFC
234  // This ensures that diaeresis and other special characters
235  // are treated uniformly on Windows and on Mac OS X
237 
238  $c = explode('/',$path);
239  for ($i = 0; $i < count($c); $i++)
240  {
241  $c[$i] = str_replace('+','%20',urlencode($c[$i]));
242  }
243  return implode('/',$c);
244  }
245 
253  public function PROPFIND(&$options, &$files)
254  {
255  // Activate tree cache
256  global $tree;
257  //$tree->useCache(true);
258 
259  $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
260  $this->writelog('PROPFIND '.$options['path']);
261 
262  // get dav object for path
263  $path =& $this->davDeslashify($options['path']);
264  $objDAV =& $this->getObject($path);
265 
266  // prepare property array
267  $files['files'] = array();
268 
269  // sanity check
270  if (is_null($objDAV)) {
271  return false;
272  }
273  if (! $objDAV->isPermitted('visible,read')) {
274  return '403 Forbidden';
275  }
276 
277  // store information for the requested path itself
278  // FIXME : create display name for object.
279  $encodedPath = $this->davUrlEncode($path);
280 
281  $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
282 
283  // information for contained resources requested?
284  if (!empty($options['depth'])) {
285  // The breadthFirst list holds the collections which we have not
286  // processed yet. If depth is infinity we append unprocessed collections
287  // to the end of this list, and remove processed collections from
288  // the beginning of this list.
289  $breadthFirst = array($objDAV);
290  $objDAV->encodedPath = $encodedPath;
291 
292  while (count($breadthFirst) > 0) {
293  // remove a collection from the beginning of the breadthFirst list
294  $collectionDAV = array_shift($breadthFirst);
295  $childrenDAV =& $collectionDAV->childrenWithPermission('visible');
296  foreach ($childrenDAV as $childDAV)
297  {
298  // On duplicate names, work with the older object (the one with the
299  // smaller object id).
300  foreach ($childrenDAV as $duplChildDAV)
301  {
302  if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
303  $duplChildDAV->getResourceName() == $childDAV->getResourceName())
304  {
305  continue 2;
306  }
307  }
308 
309  // only add visible objects to the file list
310  if (!$this->isFileHidden($childDAV))
311  {
312  $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
313  $files['files'][] =& $this->fileinfo(
314  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
315  $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
316  $childDAV
317  );
318  if ($options['depth']=='infinity' && $childDAV->isCollection()) {
319  // add a collection to the end of the breadthFirst list
320  $breadthFirst[] = $childDAV;
321  $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
322  }
323  }
324  }
325  }
326  }
327 
328  // Record read event but don't catch up with write events, because
329  // with WebDAV, a user can not see all objects contained in a folder.
331  {
332  global $ilUser;
333  ilChangeEvent::_recordReadEvent($objDAV->getObjectId(), $ilUser->getId(), false);
334  }
335  // ok, all done
336  $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
337  return true;
338  }
339 
350  private function isFileHidden(&$objDAV)
351  {
352  // Hide null resources which haven't got an active lock
353  if ($objDAV->isNullResource()) {
354  if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
355  return;
356  }
357  }
358 
359  $name = $objDAV->getResourceName();
360  $isFileHidden = false;
361  switch ($this->clientOS)
362  {
363  case 'unix' :
364  // Hide Windows thumbnail files, and files which start with '~$'.
365  $isFileHidden =
366  $name == 'Thumbs.db'
367  || substr($name, 0, 2) == '~$';
368  // Hide files which contain /
369  $isFileHidden |= preg_match('/\\//', $name);
370  break;
371  case 'windows' :
372  // Hide files that start with '.'.
373  $isFileHidden = substr($name, 0, 1) == '.';
374  // Hide files which contain \ / : * ? " < > |
375  $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
376  break;
377  default :
378  // Hide files which contain /
379  $isFileHidden |= preg_match('/\\//', $name);
380  break;
381  }
382  $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
383  return $isFileHidden;
384  }
385 
393  private function fileinfo($resourcePath, $displayPath, &$objDAV)
394  {
395  global $ilias;
396 
397  $this->writelog('fileinfo('.$resourcePath.')');
398  // create result array
399  $info = array();
400  /* Some clients, for example WebDAV-Sync, need a trailing slash at the
401  * end of a resource path to a collection.
402  * However Mac OS X does not like this!
403  */
404  if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
405  $info['path'] = $resourcePath.'/';
406  } else {
407  $info['path'] = $resourcePath;
408  }
409 
410  $info['props'] = array();
411 
412  // no special beautified displayname here ...
413  $info["props"][] =& $this->mkprop("displayname", $displayPath);
414 
415  // creation and modification time
416  $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
417  $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
418 
419  // directory (WebDAV collection)
420  $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
421  $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
422  $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
423 
424  // Only show supported locks for users who have write permission
425  if ($objDAV->isPermitted('write'))
426  {
427  $info["props"][] =& $this->mkprop("supportedlock",
428  '<D:lockentry>'
429  .'<D:lockscope><D:exclusive/></D:lockscope>'
430  .'<D:locktype><D:write/></D:locktype>'
431  .'</D:lockentry>'
432  .'<D:lockentry>'
433  .'<D:lockscope><D:shared/></D:lockscope>'
434  .'<D:locktype><D:write/></D:locktype>'
435  .'</D:lockentry>'
436  );
437  }
438 
439  // Maybe we should only show locks on objects for users who have write permission.
440  // But if we don't show these locks, users who have write permission in an object
441  // further down in a hierarchy can't see who is locking their object.
442  $locks = $this->locks->getLocksOnObjectDAV($objDAV);
443  $lockdiscovery = '';
444  foreach ($locks as $lock)
445  {
446  // DAV Clients expects to see their own owner name in
447  // the locks. Since these names are not unique (they may
448  // just be the name of the local user running the DAV client)
449  // we return the ILIAS user name in all other cases.
450  if ($lock['ilias_owner'] == $ilias->account->getId())
451  {
452  $owner = $lock['dav_owner'];
453  } else {
454  $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
455  }
456  $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
457 
458  $lockdiscovery .=
459  '<D:activelock>'
460  .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
461  //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
462  .'<D:locktype><D:write/></D:locktype>'
463  .'<D:depth>'.$lock['depth'].'</D:depth>'
464  .'<D:owner>'.$owner.'</D:owner>'
465 
466  // more than a million is considered an absolute timestamp
467  // less is more likely a relative value
468  .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
469  .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
470  .'</D:activelock>'
471  ;
472  }
473  if (strlen($lockdiscovery) > 0)
474  {
475  $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
476  }
477 
478  // get additional properties from database
479  $properties = $this->properties->getAll($objDAV);
480  foreach ($properties as $prop)
481  {
482  $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
483  }
484 
485  //$this->writelog('fileinfo():'.var_export($info, true));
486  return $info;
487  }
488 
500  public function GET(&$options)
501  {
502  global $ilUser;
503 
504  $this->writelog('GET('.var_export($options, true).')');
505  $this->writelog('GET('.$options['path'].')');
506 
507  // get dav object for path
508  $path = $this->davDeslashify($options['path']);
509  $objDAV =& $this->getObject($path);
510 
511  // sanity check
512  if (is_null($objDAV) || $objDAV->isNullResource())
513  {
514  return false;
515  }
516 
517  if (! $objDAV->isPermitted('visible,read'))
518  {
519  return '403 Forbidden';
520  }
521 
522  // is this a collection?
523  if ($objDAV->isCollection())
524  {
525  if (isset($_GET['mount']))
526  {
527  return $this->mountDir($objDAV, $options);
528  } else if (isset($_GET['mount-instructions']))
529  {
530  return $this->showMountInstructions($objDAV, $options);
531  } else {
532  return $this->getDir($objDAV, $options);
533  }
534  }
535  // detect content type
536  $options['mimetype'] =& $objDAV->getContentType();
537  // detect modification time
538  // see rfc2518, section 13.7
539  // some clients seem to treat this as a reverse rule
540  // requiring a Last-Modified header if the getlastmodified header was set
541  $options['mtime'] =& $objDAV->getModificationTimestamp();
542 
543  // detect content length
544  $options['size'] =& $objDAV->getContentLength();
545 
546  // get content as stream or as data array
547  $options['stream'] =& $objDAV->getContentStream();
548  if (is_null($options['stream']))
549  {
550  $options['data'] =& $objDAV->getContentData();
551  }
552 
553  // Record read event and catch up write events
555  {
556  ilChangeEvent::_recordReadEvent($objDAV->getObjectId(), $ilUser->getId());
557  }
558  $this->writelog('GET:'.var_export($options, true));
559 
560  return true;
561  }
573  private function mountDir(&$objDAV, &$options)
574  {
575  $path = $this->davDeslashify($options['path']);
576 
577  header('Content-Type: application/davmount+xml');
578 
579  echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
580  echo " </dm:url>".$this->base_uri."</dm:url>\n";
581 
582  $xmlPath = str_replace('&','&amp;',$path);
583  $xmlPath = str_replace('<','&lt;',$xmlPath);
584  $xmlPath = str_replace('>','&gt;',$xmlPath);
585 
586  echo " </dm:open>$xmlPath</dm:open>\n";
587  echo "</dm:mount>\n";
588 
589  exit;
590 
591  }
598  private function showMountInstructions(&$objDAV, &$options)
599  {
600  global $lng;
601 
602  $path = $this->davDeslashify($options['path']);
603 
604  // The $path variable may contain a full or a shortened DAV path.
605  // We convert it into an object path, which we can then use to
606  // construct a new full DAV path.
607  $objectPath = $this->toObjectPath($path);
608 
609  // Construct a (possibly) full DAV path from the object path.
610  $fullPath = '';
611  foreach ($objectPath as $object)
612  {
613  if ($object->getRefId() == 1 && $this->isFileHidden($object))
614  {
615  // If the repository root object is hidden, we can not
616  // create a full path, because nothing would appear in the
617  // webfolder. We resort to a shortened path instead.
618  $fullPath .= '/ref_1';
619  }
620  else
621  {
622  $fullPath .= '/'.$this->davUrlEncode($object->getResourceName());
623  }
624  }
625 
626  // Construct a shortened DAV path from the object path.
627  $shortenedPath = '/ref_'.
628  $objectPath[count($objectPath) - 1]->getRefId();
629 
630  if ($objDAV->isCollection())
631  {
632  $shortenedPath .= '/';
633  $fullPath .= '/';
634  }
635 
636  // Prepend client id to path
637  $shortenedPath = '/'.CLIENT_ID.$shortenedPath;
638  $fullPath = '/'.CLIENT_ID.$fullPath;
639 
640  // Construct webfolder URI's. The URI's are used for mounting the
641  // webfolder. Since mounting using URI's is not standardized, we have
642  // to create different URI's for different browsers.
643  $webfolderURI = $this->base_uri.$shortenedPath;
644  $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav").
645  substr($this->base_uri, strrpos($this->base_uri,':')).
646  $shortenedPath;
647  ;
648  $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav").
649  substr($this->base_uri, strrpos($this->base_uri,':')).
650  $shortenedPath
651  ;
652  $webfolderURI_IE = $this->base_uri.$shortenedPath;
653 
654  $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
655 
656  header('Content-Type: text/html; charset=UTF-8');
657  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
658  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
659  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
660  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
661  echo " <head>\n";
662  echo " <title>".sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle)."</title>\n";
663  echo " </head>\n";
664  echo " <body>\n";
665 
666  echo ilDAVServer::_getWebfolderInstructionsFor($webfolderTitle,
667  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
668  $this->clientOS,$this->clientOSFlavor);
669 
670  echo " </body>\n";
671  echo "</html>\n";
672  exit;
673  }
683  private function getDir(&$objDAV, &$options)
684  {
685  global $ilias, $lng;
686 
687  // Activate tree cache
688  global $tree;
689  //$tree->useCache(true);
690 
691  $path = $this->davDeslashify($options['path']);
692 
693  // The URL of a directory must end with a slash.
694  // If it does not we are redirecting the browser.
695  // The slash is required, because we are using relative links in the
696  // HTML code we are generating below.
697  if ($path.'/' != $options['path'])
698  {
699  header('Location: '.$this->base_uri.$path.'/');
700  exit;
701  }
702 
703  header('Content-Type: text/html; charset=UTF-8');
704 
705  // fixed width directory column format
706  $format = "%15s %-19s %-s\n";
707 
708  echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
709  echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
710  echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
711  echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
712  echo "<head>\n";
713  echo "<title>".sprintf($lng->txt('webfolder_index_of'), $path)."</title>\n";
714 
715  // Create "anchorClick" behavior for for Internet Explorer
716  // This allows to create a link to a webfolder
717  echo "<style type=\"text/css\">\n";
718  echo "<!--\n";
719  echo "a {\n";
720  echo " behavior:url(#default#AnchorClick);\n";
721  echo "}\n";
722  echo "-->\n";
723  echo "</style>\n";
724 
725  echo "</head><body>\n";
726 
727  $hrefPath = '';
728  $pathComponents = explode('/',$path);
729  $uriComponents = array();
730  foreach ($pathComponents as $component)
731  {
732  $uriComponents[] = $this->davUrlEncode($component);
733  }
734  for ($i = 0; $i < count($pathComponents); $i++)
735  {
736  $displayName = htmlspecialchars($pathComponents[$i]);
737  if ($i != 0) {
738  $hrefPath .= '/';
739  }
740  $uriPath = implode('/', array_slice($uriComponents,0,$i + 1));
741  if ($i < 2)
742  {
743  // The first two path elements consist of the webdav.php script
744  // and the client id. These elements are not part of the
745  // directory structure and thus are not represented as links.
746  $hrefPath .= $displayName;
747  }
748  else
749  {
750  $hrefPath .= '<a href="'.$this->base_uri.$uriPath.'/">'.$displayName.'</a>';
751  }
752  }
753  echo "<h3>".sprintf($lng->txt('webfolder_index_of'), $hrefPath)."</h3>\n";
754 
755  // Display user id
756  if ($ilias->account->getLogin() == 'anonymous')
757  {
758  echo "<p><font size=\"-1\">".$lng->txt('not_logged_in')."</font><br>\n";
759  } else {
760  echo "<p><font size=\"-1\">".$lng->txt('login_as')." <i>"
761  .$ilias->account->getFirstname().' '
762  .$ilias->account->getLastname().' '
763  .' '.$ilias->account->getLogin().'</i> '
764  .', '.$lng->txt('client').' <i>'.$ilias->getClientId().'</i>.'
765  ."</font><p>\n";
766  }
767 
768  // Create "open as webfolder" link
769  $href = $this->base_uri.$uriPath;
770  // IE can not mount long paths. If the path has more than one element, we
771  // create a relative path with a ref-id.
772  if (count($pathComponents) > 2)
773  {
774  $hrefIE = $this->base_uri.'/'.CLIENT_ID.'/ref_'.$objDAV->getRefId();
775  } else {
776  $hrefIE = $href;
777  }
778  echo "<p><font size=\"-1\">".
779  sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions").
780  "</font></p>\n";
781  echo "<p><font size=\"-1\">".
782  sprintf($lng->txt('webfolder_mount_dir_with'),
783  "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
784  'webdav'.substr($href,4), // Konqueror
785  'dav'.substr($href,4), // Nautilus
786  $href.'?mount' // RFC 4709
787  )
788  ."</font></p>\n";
789 
790  echo "<pre>";
791  printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
792  echo "<hr>";
793 
794  $collectionCount = 0;
795  $fileCount = 0;
796  $children =& $objDAV->childrenWithPermission('visible');
797  foreach ($children as $childDAV) {
798  if ($childDAV->isCollection() && !$this->isFileHidden($childDAV))
799  {
800  $collectionCount++;
801  $name = $this->davUrlEncode($childDAV->getResourceName());
802  printf($format,
803  '-',
804  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
805  '<a href="'.$name.'/'.'">'.$childDAV->getDisplayName()."</a>");
806  }
807  }
808  foreach ($children as $childDAV) {
809  if ($childDAV->isFile() && !$this->isFileHidden($childDAV))
810  {
811  $fileCount++;
812  $name = $this->davUrlEncode($childDAV->getResourceName());
813  printf($format,
814  number_format($childDAV->getContentLength()),
815  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
816  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
817  }
818  }
819  foreach ($children as $childDAV) {
820  if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV))
821  {
822  $name = $this->davUrlEncode($childDAV->getResourceName());
823  printf($format,
824  'Lock',
825  strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
826  '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
827  }
828  }
829  echo "<hr>";
830  echo $collectionCount.' '.$lng->txt(($collectionCount == 1) ? 'folder' : 'folders').', ';
831  echo $fileCount.' '.$lng->txt(($fileCount == 1) ? 'file' : 'files').'.';
832  echo "</pre>";
833  echo "</body></html>\n";
834 
835  exit;
836  }
837 
838 
845  public function PUT(&$options)
846  {
847  global $ilUser;
848 
849  $this->writelog('PUT('.var_export($options, true).')');
850 
851  $path = $this->davDeslashify($options['path']);
852  $parent = dirname($path);
853  $name = $this->davBasename($path);
854 
855  // get dav object for path
856  $parentDAV =& $this->getObject($parent);
857 
858  // sanity check
859  if (is_null($parentDAV) || ! $parentDAV->isCollection()) {
860  return '409 Conflict';
861  }
862 
863  // Prevent putting of files which exceed upload limit
864  // FIXME: since this is an optional parameter, we should to do the
865  // same check again in function PUTfinished.
866  if ($options['content_length'] != null &&
867  $options['content_length'] > $this->getUploadMaxFilesize()) {
868 
869  $this->writelog('PUT is forbidden, because content length='.
870  $options['content_length'].' is larger than upload_max_filesize='.
871  $this->getUploadMaxFilesize().'in php.ini');
872 
873  return '403 Forbidden';
874  }
875 
876  // determine mime type
877  include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
878  $mime = ilMimeTypeUtil::getMimeType("", $name, $options['content_type']);
879 
880  $objDAV =& $this->getObject($path);
881  if (is_null($objDAV))
882  {
883  $ttype = $parentDAV->getILIASFileType();
884  $isperm = $parentDAV->isPermitted('create', $ttype);
885  if (! $isperm)
886  {
887  $this->writelog('PUT is forbidden, because user has no create permission');
888 
889  return '403 Forbidden';
890  }
891  $options["new"] = true;
892  $objDAV =& $parentDAV->createFile($name);
893  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
894  //$objDAV->setContentType($options['content_type']);
895  $objDAV->setContentType($mime);
896  if ($options['content_length'] != null)
897  {
898  $objDAV->setContentLength($options['content_length']);
899  }
900  $objDAV->write();
901  // Record write event
903  {
904  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
905  }
906  }
907  else if ($objDAV->isNullResource())
908  {
909  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
910  {
911  $this->writelog('PUT is forbidden, because user has no create permission');
912  return '403 Forbidden';
913  }
914  $options["new"] = false;
915  $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
916  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
917  //$objDAV->setContentType($options['content_type']);
918  $objDAV->setContentType($mime);
919  if ($options['content_length'] != null)
920  {
921  $objDAV->setContentLength($options['content_length']);
922  }
923  $objDAV->write();
924 
925  // Record write event
927  {
928  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
929  }
930  }
931  else
932  {
933  if (! $objDAV->isPermitted('write'))
934  {
935  $this->writelog('PUT is forbidden, because user has no write permission');
936  return '403 Forbidden';
937  }
938  $options["new"] = false;
939  $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type'].' content_length='.$options['content_length']);
940 
941  // Create a new version if the previous version is not empty
942  if ($objDAV->getContentLength() != 0) {
943  $objDAV->createNewVersion();
944  }
945 
946  //$objDAV->setContentType($options['content_type']);
947  $objDAV->setContentType($mime);
948  if ($options['content_length'] != null)
949  {
950  $objDAV->setContentLength($options['content_length']);
951  }
952  $objDAV->write();
953 
954  // Record write event
956  {
957  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
958  ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
959  }
960  }
961  // store this object, we reuse it in method PUTfinished
962  $this->putObjDAV = $objDAV;
963 
964  $out =& $objDAV->getContentOutputStream();
965  $this->writelog('PUT outputstream='.$out);
966 
967  return $out;
968  }
969 
976  public function PUTfinished(&$options)
977  {
978  $this->writelog('PUTfinished('.var_export($options, true).')');
979 
980  // Update the content length in the file object, if the
981  // the client did not specify a content_length
982  if ($options['content_length'] == null)
983  {
984  $objDAV = $this->putObjDAV;
985  $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
986  $objDAV->write();
987  $this->putObjDAV = null;
988  }
989  return true;
990  }
991 
992 
999  public function MKCOL($options)
1000  {
1001  global $ilUser;
1002 
1003  $this->writelog('MKCOL('.var_export($options, true).')');
1004  $this->writelog('MKCOL '.$options['path']);
1005 
1006  $path =& $this->davDeslashify($options['path']);
1007  $parent =& dirname($path);
1008  $name =& $this->davBasename($path);
1009 
1010  // No body parsing yet
1011  if(!empty($_SERVER["CONTENT_LENGTH"])) {
1012  return "415 Unsupported media type";
1013  }
1014 
1015  // Check if an object with the path already exists.
1016  $objDAV =& $this->getObject($path);
1017  if (! is_null($objDAV))
1018  {
1019  return '405 Method not allowed';
1020  }
1021 
1022  // get parent dav object for path
1023  $parentDAV =& $this->getObject($parent);
1024 
1025  // sanity check
1026  if (is_null($parentDAV) || ! $parentDAV->isCollection())
1027  {
1028  return '409 Conflict';
1029  }
1030 
1031  if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1032  {
1033  return '403 Forbidden';
1034  }
1035 
1036  // XXX Implement code that Handles null resource here
1037 
1038  $objDAV = $parentDAV->createCollection($name);
1039 
1040  if ($objDAV != null)
1041  {
1042  // Record write event
1044  {
1045  ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1046  }
1047  }
1048 
1049  $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1050  return $result;
1051  }
1052 
1053 
1060  public function DELETE($options)
1061  {
1062  global $ilUser;
1063 
1064  $this->writelog('DELETE('.var_export($options, true).')');
1065  $this->writelog('DELETE '.$options['path']);
1066 
1067  // get dav object for path
1068  $path =& $this->davDeslashify($options['path']);
1069  $parentDAV =& $this->getObject(dirname($path));
1070  $objDAV =& $this->getObject($path);
1071 
1072  // sanity check
1073  if (is_null($objDAV) || $objDAV->isNullResource())
1074  {
1075  return '404 Not Found';
1076  }
1077  if (! $objDAV->isPermitted('delete'))
1078  {
1079  return '403 Forbidden';
1080  }
1081 
1082  $parentDAV->remove($objDAV);
1083 
1084  // Record write event
1086  {
1087  ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1088  }
1089 
1090  return '204 No Content';
1091  }
1092 
1099  public function MOVE($options)
1100  {
1101  global $ilUser;
1102 
1103  $this->writelog('MOVE('.var_export($options, true).')');
1104  $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1105 
1106  // Get path names
1107  $src = $this->davDeslashify($options['path']);
1108  $srcParent = dirname($src);
1109  $srcName = $this->davBasename($src);
1110  $dst = $this->davDeslashify($options['dest']);
1111 
1112  $dstParent = dirname($dst);
1113  $dstName = $this->davBasename($dst);
1114  $this->writelog('move '.$dst.' dstname='.$dstName);
1115  // Source and destination must not be the same
1116  if ($src == $dst)
1117  {
1118  return '409 Conflict (source and destination are the same)';
1119  }
1120 
1121  // Destination must not be in a subtree of source
1122  if (substr($dst,strlen($src)+1) == $src.'/')
1123  {
1124  return '409 Conflict (destination is in subtree of source)';
1125  }
1126 
1127  // Get dav objects for path
1128  $srcDAV =& $this->getObject($src);
1129  $dstDAV =& $this->getObject($dst);
1130  $srcParentDAV =& $this->getObject($srcParent);
1131  $dstParentDAV =& $this->getObject($dstParent);
1132 
1133  // Source must exist
1134  if ($srcDAV == null)
1135  {
1136  return '409 Conflict (source does not exist)';
1137  }
1138 
1139  // Overwriting is only allowed, if overwrite option is set to 'T'
1140  $isOverwritten = false;
1141  if ($dstDAV != null)
1142  {
1143  if ($options['overwrite'] == 'T')
1144  {
1145  // Delete the overwritten destination
1146  if ($dstDAV->isPermitted('delete'))
1147  {
1148  $dstParentDAV->remove($dstDAV);
1149  $dstDAV = null;
1150  $isOverwritten = true;
1151  } else {
1152  return '403 Not Permitted';
1153  }
1154  } else {
1155  return '412 Precondition Failed';
1156  }
1157  }
1158 
1159  // Parents of destination must exist
1160  if ($dstParentDAV == null)
1161  {
1162  return '409 Conflict (parent of destination does not exist)';
1163  }
1164 
1165  if ($srcParent == $dstParent)
1166  {
1167  // Rename source, if source and dest are in same parent
1168 
1169  // Check permission
1170  if (! $srcDAV->isPermitted('write'))
1171  {
1172  return '403 Forbidden';
1173  }
1174  $this->writelog('rename dstName='.$dstName);
1175  $srcDAV->setResourceName($dstName);
1176  $srcDAV->write();
1177  } else {
1178  // Move source, if source and dest are in same parent
1179 
1180 
1181  if (! $srcDAV->isPermitted('delete'))
1182  {
1183  return '403 Forbidden';
1184  }
1185 
1186  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1187  {
1188  return '403 Forbidden';
1189  }
1190  $dstParentDAV->addMove($srcDAV, $dstName);
1191  }
1192 
1193  // Record write event
1195  {
1196  if ($isOverwritten)
1197  {
1198  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1199  }
1200  else
1201  {
1202  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1203  ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1204  }
1205  }
1206 
1207  return ($isOverwritten) ? '204 No Content' : '201 Created';
1208  }
1209 
1216  public function COPY($options, $del=false)
1217  {
1218  global $ilUser;
1219  $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1220  $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1221 
1222  // no copying to different WebDAV Servers
1223  if (isset($options["dest_url"])) {
1224  return "502 bad gateway";
1225  }
1226 
1227  $src = $this->davDeslashify($options['path']);
1228  $srcParent = dirname($src);
1229  $srcName = $this->davBasename($src);
1230  $dst = $this->davDeslashify($options['dest']);
1231  $dstParent = dirname($dst);
1232  $dstName = $this->davBasename($dst);
1233 
1234  // sanity check
1235  if ($src == $dst)
1236  {
1237  return '409 Conflict'; // src and dst are the same
1238  }
1239 
1240  if (substr($dst,strlen($src)+1) == $src.'/')
1241  {
1242  return '409 Conflict'; // dst is in subtree of src
1243  }
1244 
1245  $this->writelog('COPY src='.$src.' dst='.$dst);
1246  // get dav object for path
1247  $srcDAV =& $this->getObject($src);
1248  $dstDAV =& $this->getObject($dst);
1249  $dstParentDAV =& $this->getObject($dstParent);
1250 
1251  if (is_null($srcDAV) || $srcDAV->isNullResource())
1252  {
1253  return '409 Conflict'; // src does not exist
1254  }
1255  if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1256  {
1257  return '409 Conflict'; // parent of dst does not exist
1258  }
1259  $isOverwritten = false;
1260 
1261  // XXX Handle nulltype for dstDAV
1262  if (! is_null($dstDAV))
1263  {
1264  if ($options['overwrite'] == 'T')
1265  {
1266  if ($dstDAV->isPermitted('delete'))
1267  {
1268  $dstParentDAV->remove($dstDAV);
1270  {
1271  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1272  }
1273 
1274  $dstDAV = null;
1275  $isOverwritten = true;
1276  } else {
1277  return '403 Forbidden';
1278  }
1279  } else {
1280  return '412 Precondition Failed';
1281  }
1282  }
1283 
1284  if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1285  {
1286  return '403 Forbidden';
1287  }
1288  $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1289 
1290  // Record write event
1292  {
1293  ilChangeEvent::_recordReadEvent($srcDAV->getObjectId(), $ilUser->getId());
1294  ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1295  }
1296 
1297  return ($isOverwritten) ? '204 No Content' : '201 Created';
1298  }
1299 
1306  public function PROPPATCH(&$options)
1307  {
1308  $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1309  $this->writelog('PROPPATCH '.$options['path']);
1310 
1311  // get dav object for path
1312  $path =& $this->davDeslashify($options['path']);
1313  $objDAV =& $this->getObject($path);
1314 
1315  // sanity check
1316  if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1317 
1318  $isPermitted = $objDAV->isPermitted('write');
1319  foreach($options['props'] as $key => $prop) {
1320  if (!$isPermitted || $prop['ns'] == 'DAV:')
1321  {
1322  $options['props'][$key]['status'] = '403 Forbidden';
1323  } else {
1324  $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1325  }
1326  }
1327 
1328  return "";
1329  }
1330 
1331 
1338  public function LOCK(&$options)
1339  {
1340  global $ilias;
1341  $this->writelog('LOCK('.var_export($options, true).')');
1342  $this->writelog('LOCK '.$options['path']);
1343 
1344  // Check if an object with the path exists.
1345  $path =& $this->davDeslashify($options['path']);
1346  $objDAV =& $this->getObject($path);
1347  // Handle null-object locking
1348  // --------------------------
1349  if (is_null($objDAV))
1350  {
1351  $this->writelog('LOCK handling null-object locking...');
1352 
1353  // If the name does not exist, we create a null-object for it
1354  if (isset($options["update"]))
1355  {
1356  $this->writelog('LOCK lock-update failed on non-existing null-object.');
1357  return '412 Precondition Failed';
1358  }
1359 
1360  $parent = dirname($path);
1361  $parentDAV =& $this->getObject($parent);
1362  if (is_null($parentDAV))
1363  {
1364  $this->writelog('LOCK lock failed on non-existing path to null-object.');
1365  return '404 Not Found';
1366  }
1367  if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1368  ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1369  {
1370  $this->writelog('LOCK lock failed - creation of null object not permitted.');
1371  return '403 Forbidden';
1372  }
1373 
1374  $objDAV =& $parentDAV->createNull($this->davBasename($path));
1375  $this->writelog('created null resource for '.$path);
1376  }
1377 
1378  // ---------------------
1379  if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1380  {
1381  $this->writelog('LOCK lock failed - user has no write permission.');
1382  return '403 Forbidden';
1383  }
1384 
1385  // XXX - Check if there are other locks on the resource
1386  if (!isset($options['timeout']) || is_array($options['timeout']))
1387  {
1388  $options["timeout"] = time()+360; // 6min.
1389  }
1390 
1391  if(isset($options["update"])) { // Lock Update
1392  $this->writelog('LOCK update token='.var_export($options,true));
1393  $success = $this->locks->updateLockWithoutCheckingDAV(
1394  $objDAV,
1395  $options['update'],
1396  $options['timeout']
1397  );
1398  if ($success)
1399  {
1400  $data = $this->locks->getLockDAV($objDAV, $options['update']);
1401  if ($data['ilias_owner'] == $ilias->account->getId())
1402  {
1403  $owner = $data['dav_owner'];
1404  } else {
1405  $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1406  }
1407  $options['owner'] = $owner;
1408  $options['locktoken'] = $data['token'];
1409  $options['timeout'] = $data['expires'];
1410  $options['depth'] = $data['depth'];
1411  $options['scope'] = $data['scope'];
1412  $options['type'] = $data['scope'];
1413  }
1414 
1415  } else {
1416  $this->writelog('LOCK create new lock');
1417 
1418  // XXX - Attempting to create a recursive exclusive lock
1419  // on a collection must fail, if any of nodes in the subtree
1420  // of the collection already has a lock.
1421  // XXX - Attempting to create a recursive shared lock
1422  // on a collection must fail, if any of nodes in the subtree
1423  // of the collection already has an exclusive lock.
1424  //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1425  $this->writelog('lock owner='.$owner);
1426  $success = $this->locks->lockWithoutCheckingDAV(
1427  $objDAV,
1428  $ilias->account->getId(),
1429  trim($options['owner']),
1430  $options['locktoken'],
1431  $options['timeout'],
1432  $options['depth'],
1433  $options['scope']
1434  );
1435  }
1436 
1437  // Note: As a workaround for the Microsoft WebDAV Client, we return
1438  // true/false here (resulting in the status '200 OK') instead of
1439  // '204 No Content').
1440  //return ($success) ? '204 No Content' : false;
1441  return $success;
1442  }
1443 
1450  public function UNLOCK(&$options)
1451  {
1452  global $log, $ilias;
1453  $this->writelog('UNLOCK(options='.var_export($options, true).')');
1454  $this->writelog('UNLOCK '.$options['path']);
1455 
1456  // Check if an object with the path exists.
1457  $path =& $this->davDeslashify($options['path']);
1458  $objDAV =& $this->getObject($path);
1459  if (is_null($objDAV)) {
1460  return '404 Not Found';
1461  }
1462  if (! $objDAV->isPermitted('write')) {
1463  return '403 Forbidden';
1464  }
1465 
1466  $success = $this->locks->unlockWithoutCheckingDAV(
1467  $objDAV,
1468  $options['token']
1469  );
1470 
1471  // Delete null resource object if there are no locks associated to
1472  // it anymore
1473  if ($objDAV->isNullResource()
1474  && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1475  {
1476  $parent = dirname($this->davDeslashify($options['path']));
1477  $parentDAV =& $this->getObject($parent);
1478  $parentDAV->remove($objDAV);
1479  }
1480 
1481  // Workaround for Mac OS X: We must return 200 here instead of
1482  // 204.
1483  //return ($success) ? '204 No Content' : '412 Precondition Failed';
1484  return ($success) ? '200 OK' : '412 Precondition Failed';
1485  }
1486 
1499  protected function checkLock($path)
1500  {
1501  global $ilias;
1502 
1503  $this->writelog('checkLock('.$path.')');
1504  $result = null;
1505 
1506  // get dav object for path
1507  //$objDAV = $this->getObject($path);
1508 
1509  // convert DAV path into ilObjectDAV path
1510  $objPath = $this->toObjectPath($path);
1511  if (! is_null($objPath))
1512  {
1513  $objDAV = $objPath[count($objPath) - 1];
1514  $locks = $this->locks->getLocksOnPathDAV($objPath);
1515  foreach ($locks as $lock)
1516  {
1517  $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1518  && $lock['node_id'] == $objDAV->getNodeId();
1519 
1520  // Check all locks on last object in path,
1521  // but only locks with depth infinity on parent objects.
1522  if ($isLastPathComponent || $lock['depth'] == 'infinity')
1523  {
1524  // DAV Clients expects to see their own owner name in
1525  // the locks. Since these names are not unique (they may
1526  // just be the name of the local user running the DAV client)
1527  // we return the ILIAS user name in all other cases.
1528  if ($lock['ilias_owner'] == $ilias->account->getId())
1529  {
1530  $owner = $lock['dav_owner'];
1531  } else {
1532  $owner = $this->getLogin($lock['ilias_owner']);
1533  }
1534 
1535  // FIXME - Shouldn't we collect all locks instead of
1536  // using an arbitrary one?
1537  $result = array(
1538  "type" => "write",
1539  "obj_id" => $lock['obj_id'],
1540  "node_id" => $lock['node_id'],
1541  "scope" => $lock['scope'],
1542  "depth" => $lock['depth'],
1543  "owner" => $owner,
1544  "token" => $lock['token'],
1545  "expires" => $lock['expires']
1546  );
1547  if ($lock['scope'] == 'exclusive')
1548  {
1549  // If there is an exclusive lock in the path, it
1550  // takes precedence over all non-exclusive locks in
1551  // parent nodes. Therefore we can can finish collecting
1552  // locks.
1553  break;
1554  }
1555  }
1556  }
1557  }
1558  $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1559 
1560  return $result;
1561  }
1562 
1567  protected function getLogin($userId)
1568  {
1569  $login = ilObjUser::_lookupLogin($userId);
1570  $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1571  return $login;
1572  }
1573 
1574 
1581  private function getObject($davPath)
1582  {
1583  global $tree;
1584 
1585 
1586  // If the second path elements starts with 'file_', the following
1587  // characters of the path element directly identify the ref_id of
1588  // a file object.
1589  $davPathComponents = split('/',substr($davPath,1));
1590  if (count($davPathComponents) > 1 &&
1591  substr($davPathComponents[1],0,5) == 'file_')
1592  {
1593  $ref_id = substr($davPathComponents[1],5);
1594  $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1595 
1596  // Poor IE needs this, in order to successfully display
1597  // PDF documents
1598  header('Pragma: private');
1599  }
1600  else
1601  {
1602  $nodePath = $this->toNodePath($davPath);
1603  if ($nodePath == null && count($davPathComponents) == 1)
1604  {
1605  return ilObjectDAV::createObject(-1,'mountPoint');
1606  }
1607  }
1608  if (is_null($nodePath))
1609  {
1610  return null;
1611  } else {
1612  $top = $nodePath[count($nodePath) - 1];
1613  return ilObjectDAV::createObject($top['child'],$top['type']);
1614  }
1615  }
1622  private function toObjectPath($davPath)
1623  {
1624  $this->writelog('toObjectPath('.$davPath);
1625  global $tree;
1626 
1627  $nodePath = $this->toNodePath($davPath);
1628 
1629  if (is_null($nodePath))
1630  {
1631  return null;
1632  } else {
1633  $objectPath = array();
1634  foreach ($nodePath as $node)
1635  {
1636  $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1637  if (is_null($pathElement))
1638  {
1639  break;
1640  }
1641  $objectPath[] = $pathElement;
1642  }
1643  return $objectPath;
1644  }
1645  }
1646 
1658  public function toNodePath($davPath)
1659  {
1660  global $tree;
1661  $this->writelog('toNodePath('.$davPath.')...');
1662 
1663  // Split the davPath into path titles
1664  $titlePath = split('/',substr($davPath,1));
1665 
1666  // Remove the client id from the beginning of the title path
1667  if (count($titlePath) > 0)
1668  {
1669  array_shift($titlePath);
1670  }
1671 
1672  // If the last path title is empty, remove it
1673  if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1674  {
1675  array_pop($titlePath);
1676  }
1677 
1678  // If the path is empty, return null
1679  if (count($titlePath) == 0)
1680  {
1681  $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1682  return null;
1683  }
1684 
1685  // If the path is an absolute path, ref_id is null.
1686  $ref_id = null;
1687 
1688  // If the path is a relative folder path, convert it into an absolute path
1689  if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1690  {
1691  $ref_id = substr($titlePath[0],4);
1692  array_shift($titlePath);
1693  }
1694 
1695  $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1696 
1697  $this->writelog('toNodePath():'.var_export($nodePath,true));
1698  return $nodePath;
1699  }
1700 
1707  private function davDeslashify($path)
1708  {
1710 
1711  if ($path[strlen($path)-1] == '/') {
1712  $path = substr($path,0, strlen($path) - 1);
1713  }
1714  $this->writelog('davDeslashify:'.$path);
1715  return $path;
1716  }
1717 
1724  private function davBasename($path)
1725  {
1726  $components = split('/',$path);
1727  return count($components) == 0 ? '' : $components[count($components) - 1];
1728  }
1729 
1736  protected function writelog($message)
1737  {
1738  // Only write log message, if we are in debug mode
1739  if ($this->isDebug)
1740  {
1741  global $ilLog, $ilias;
1742  if ($ilLog)
1743  {
1744  if ($message == '---')
1745  {
1746  $ilLog->write('');
1747  } else {
1748  $ilLog->write(
1749  $ilias->account->getLogin()
1750  .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1751  .' ilDAVServer.'.str_replace("\n",";",$message)
1752  );
1753  }
1754  }
1755  else
1756  {
1757  $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1758  fwrite($fh, date('Y-m-d H:i:s '));
1759  fwrite($fh, str_replace("\n",";",$message));
1760  fwrite($fh, "\n\n");
1761  fclose($fh);
1762  }
1763  }
1764  }
1765 
1778  function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1779  {
1780  if ($genericURI) {
1781  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1782  $query = null;
1783  } else if ($this->clientOS == 'windows') {
1784  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1785  $query = 'mount-instructions';
1786  } else if ($this->clientBrowser == 'konqueror') {
1787  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1788  $query = null;
1789  } else if ($this->clientBrowser == 'nautilus') {
1790  $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1791  $query = null;
1792  } else {
1793  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1794  $query = 'mount-instructions';
1795  }
1796  $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1797  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1798 
1799  $uri = $baseUri.'/ref_'.$refId.'/';
1800  if ($query != null)
1801  {
1802  $uri .= '?'.$query;
1803  }
1804 
1805  return $uri;
1806  }
1821  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  } else if ($this->clientBrowser == 'konqueror') {
1827  $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1828  $query = null;
1829  } else if ($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  {
1842  $uri .= '?'.$query;
1843  }
1844 
1845  return $uri;
1846  }
1858  public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1859  {
1860  $nodeId = 0;
1861  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1862  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1863  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1864 
1865  if (! is_null($ressourceName) && ! is_null($parentRefId))
1866  {
1867  // Quickly create URI from the known data without needing SQL queries
1868  $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1869  } else {
1870  // Create URI and use some SQL queries to get the missing data
1871  global $tree;
1872  $nodePath = $tree->getNodePath($refId);
1873 
1874  if (is_null($nodePath) || count($nodePath) < 2)
1875  {
1876  // No object path? Return null - file is not in repository.
1877  $uri = null;
1878  } else {
1879  $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1880  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1881  }
1882  }
1883  return $uri;
1884  }
1885 
1903  public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1904  {
1905  $nodeId = 0;
1906  $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1907  "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1908  $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1909 
1910  if (! is_null($ressourceName) && ! is_null($parentRefId))
1911  {
1912  // Quickly create URI from the known data without needing SQL queries
1913  $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1914  } else {
1915  // Create URI and use some SQL queries to get the missing data
1916  global $tree;
1917  $nodePath = $tree->getNodePath($refId);
1918 
1919  if (is_null($nodePath) || count($nodePath) < 2)
1920  {
1921  // No object path? Return null - file is not in repository.
1922  $uri = null;
1923  } else {
1924  $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1925  $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1926  }
1927  }
1928  return $uri;
1929  }
1930 
1936  public function isWebDAVoverHTTPS() {
1937  if ($this->isHTTPS == null) {
1938  global $ilSetting;
1939  require_once 'classes/class.ilHTTPS.php';
1940  $https = new ilHTTPS();
1941  $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1942  }
1943  return $this->isHTTPS;
1944  }
1945 
1954  public static function _isActive()
1955  {
1956  global $ilClientIniFile;
1957  return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1958  @include_once("Auth/HTTP.php");
1959  }
1965  public static function _isActionsVisible()
1966  {
1967  global $ilClientIniFile;
1968  return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
1969  }
1970 
1980  public static function _getDefaultWebfolderInstructions()
1981  {
1982  global $lng;
1983  return $lng->txt('webfolder_instructions_text');
1984  }
1985 
2012  public static function _getWebfolderInstructionsFor($webfolderTitle,
2013  $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2014  $os = 'unknown', $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'))
2021  {
2023  }
2024 
2025  $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2026  $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2027  $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2028  $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2029  $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2030  $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2031 
2032  switch ($os)
2033  {
2034  case 'windows' :
2035  $operatingSystem = 'WINDOWS';
2036  break;
2037  case 'unix' :
2038  switch ($osFlavor)
2039  {
2040  case 'osx' :
2041  $operatingSystem = 'MAC';
2042  break;
2043  case 'linux' :
2044  $operatingSystem = 'LINUX';
2045  break;
2046  default :
2047  $operatingSystem = 'LINUX';
2048  break;
2049  }
2050  break;
2051  default :
2052  $operatingSystem = 'UNKNOWN';
2053  break;
2054  }
2055 
2056  if ($operatingSystem != 'UNKNOWN')
2057  {
2058  $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2059  $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2060  }
2061  else
2062  {
2063  $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2064  }
2065  return $str;
2066  }
2067 
2073  private function getUploadMaxFilesize() {
2074  $val = ini_get('upload_max_filesize');
2075 
2076  $val = trim($val);
2077  $last = strtolower($val[strlen($val)-1]);
2078  switch($last) {
2079  // The 'G' modifier is available since PHP 5.1.0
2080  case 'g':
2081  $val *= 1024;
2082  case 'm':
2083  $val *= 1024;
2084  case 'k':
2085  $val *= 1024;
2086  }
2087 
2088  return $val;
2089  }
2090 }
2091 // END WebDAV
2092 ?>