ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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
25require_once 'Services/WebDAV/classes/Server.php';
26require_once 'Services/WebDAV/classes/class.ilDAVLocks.php';
27require_once 'Services/WebDAV/classes/class.ilDAVProperties.php';
28require_once 'Services/WebDAV/classes/class.ilObjectDAV.php';
29
30require_once 'Services/User/classes/class.ilObjUser.php';
31require_once 'Services/Utilities/classes/class.ilFileUtils.php';
32require_once 'include/Unicode/UtfNormal.php';
33require_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
126 public function ilDAVServer()
127 {
128 $this->writelog("<constructor>");
129
130 // Initialize the WebDAV server and create
131 // locking and property support objects
132 $this->HTTP_WebDAV_Server();
133 $this->locks = new ilDAVLocks();
134 $this->properties = new ilDAVProperties();
135 //$this->locks->createTable();
136 //$this->properties->createTable();
137
138 // Guess operating system, operating system flavor and browser of the webdav client
139 //
140 // - We need to know the operating system in order to properly
141 // hide hidden resources in directory listings.
142 //
143 // - We need the operating system flavor and the browser to
144 // properly support mounting of a webdav folder.
145 //
146 $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
147 $this->writelog('userAgent='.$userAgent);
148 if (strpos($userAgent,'windows') !== false
149 || strpos($userAgent,'microsoft') !== false)
150 {
151 $this->clientOS = 'windows';
152 if(strpos($userAgent,'nt 5.1') !== false){
153 $this->clientOSFlavor = 'xp';
154 }else{
155 $this->clientOSFlavor = 'nichtxp';
156 }
157
158 } else if (strpos($userAgent,'darwin') !== false
159 || strpos($userAgent,'macintosh') !== false
160 || strpos($userAgent,'linux') !== false
161 || strpos($userAgent,'solaris') !== false
162 || strpos($userAgent,'aix') !== false
163 || strpos($userAgent,'unix') !== false
164 || strpos($userAgent,'gvfs') !== false // nautilus browser uses this ID
165 )
166 {
167 $this->clientOS = 'unix';
168 if (strpos($userAgent,'linux') !== false)
169 {
170 $this->clientOSFlavor = 'linux';
171 }
172 else if (strpos($userAgent,'macintosh') !== false)
173 {
174 $this->clientOSFlavor = 'osx';
175 }
176 }
177 if (strpos($userAgent,'konqueror') !== false)
178 {
179 $this->clientBrowser = 'konqueror';
180 }
181 }
182
187 public static function getInstance()
188 {
189 if(self::$instance != NULL)
190 {
191 return self::$instance;
192 }
193 return self::$instance = new ilDAVServer();
194 }
195
199 public function serveRequest()
200 {
201 // die quickly if plugin is deactivated
202 if (!self::_isActive())
203 {
204 $this->writelog(__METHOD__.' WebDAV disabled. Aborting');
205 $this->http_status('403 Forbidden');
206 echo '<html><body><h1>Sorry</h1>'.
207 '<p><b>Please enable the WebDAV plugin in the ILIAS Administration panel.</b></p>'.
208 '<p>You can only access this page, if WebDAV is enabled on this server.</p>'.
209 '</body></html>';
210 return;
211 }
212
213 try {
214 $start = time();
215 $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['PATH_INFO'].' ...');
216 parent::serveRequest();
217 $end = time();
218 $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' done status='.$this->_http_status.' elapsed='.($end - $start));
219 $this->writelog('---');
220 }
221 catch (Exception $e)
222 {
223 $this->writelog('serveRequest():'.$_SERVER['REQUEST_METHOD'].' caught exception: '.$e->getMessage().'\n'.$e->getTraceAsString());
224 }
225 }
226
261 private function davUrlEncode($path)
262 {
263 // We compose the path to Unicode Normal Form NFC
264 // This ensures that diaeresis and other special characters
265 // are treated uniformly on Windows and on Mac OS X
267
268 $c = explode('/',$path);
269 for ($i = 0; $i < count($c); $i++)
270 {
271 $c[$i] = str_replace('+','%20',urlencode($c[$i]));
272 }
273 return implode('/',$c);
274 }
275
283 public function PROPFIND(&$options, &$files)
284 {
285 // Activate tree cache
286 global $tree;
287 //$tree->useCache(true);
288
289 $this->writelog('PROPFIND(options:'.var_export($options, true).' files:'.var_export($files, true).'.)');
290 $this->writelog('PROPFIND '.$options['path']);
291
292 // get dav object for path
293 $path =& $this->davDeslashify($options['path']);
294 $objDAV =& $this->getObject($path);
295
296 // prepare property array
297 $files['files'] = array();
298
299 // sanity check
300 if (is_null($objDAV)) {
301 return false;
302 }
303 if (! $objDAV->isPermitted('visible,read')) {
304 return '403 Forbidden';
305 }
306
307 // store information for the requested path itself
308 // FIXME : create display name for object.
309 $encodedPath = $this->davUrlEncode($path);
310
311 $GLOBALS['ilLog']->write(print_r($encodedPath,true));
312
313 $files['files'][] =& $this->fileinfo($encodedPath, $encodedPath, $objDAV);
314
315 // information for contained resources requested?
316 if (!empty($options['depth'])) {
317 // The breadthFirst list holds the collections which we have not
318 // processed yet. If depth is infinity we append unprocessed collections
319 // to the end of this list, and remove processed collections from
320 // the beginning of this list.
321 $breadthFirst = array($objDAV);
322 $objDAV->encodedPath = $encodedPath;
323
324 while (count($breadthFirst) > 0) {
325 // remove a collection from the beginning of the breadthFirst list
326 $collectionDAV = array_shift($breadthFirst);
327 $childrenDAV =& $collectionDAV->childrenWithPermission('visible,read');
328 foreach ($childrenDAV as $childDAV)
329 {
330 // On duplicate names, work with the older object (the one with the
331 // smaller object id).
332 foreach ($childrenDAV as $duplChildDAV)
333 {
334 if ($duplChildDAV->getObjectId() < $childDAV->getObjectId() &&
335 $duplChildDAV->getResourceName() == $childDAV->getResourceName())
336 {
337 continue 2;
338 }
339 }
340
341 // only add visible objects to the file list
342 if (!$this->isFileHidden($childDAV))
343 {
344 $this->writelog('PROPFIND() child ref_id='.$childDAV->getRefId());
345 $files['files'][] =& $this->fileinfo(
346 $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName()),
347 $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getDisplayName()),
348 $childDAV
349 );
350 if ($options['depth']=='infinity' && $childDAV->isCollection()) {
351 // add a collection to the end of the breadthFirst list
352 $breadthFirst[] = $childDAV;
353 $childDAV->encodedPath = $collectionDAV->encodedPath.'/'.$this->davUrlEncode($childDAV->getResourceName());
354 }
355 }
356 }
357 }
358 }
359
360 // Record read event but don't catch up with write events, because
361 // with WebDAV, a user can not see all objects contained in a folder.
362 global $ilUser;
363 ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
364 $objDAV->getObjectId(), $ilUser->getId(), false);
365
366 // ok, all done
367 $this->writelog('PROPFIND():true options='.var_export($options, true).' files='.var_export($files,true));
368 return true;
369 }
370
381 private function isFileHidden(&$objDAV)
382 {
383 // Hide null resources which haven't got an active lock
384 if ($objDAV->isNullResource()) {
385 if (count($this->locks->getLocksOnObjectDAV($objDAV)) == 0) {
386 return;
387 }
388 }
389
390 $name = $objDAV->getResourceName();
391 $isFileHidden = false;
392 switch ($this->clientOS)
393 {
394 case 'unix' :
395 // Hide Windows thumbnail files, and files which start with '~$'.
396 $isFileHidden =
397 $name == 'Thumbs.db'
398 || substr($name, 0, 2) == '~$';
399 // Hide files which contain /
400 $isFileHidden |= preg_match('/\\//', $name);
401 break;
402 case 'windows' :
403 // Hide files that start with '.'.
404 $isFileHidden = substr($name, 0, 1) == '.';
405 // Hide files which contain \ / : * ? " < > |
406 $isFileHidden |= preg_match('/\\\\|\\/|:|\\*|\\?|"|<|>|\\|/', $name);
407 break;
408 default :
409 // Hide files which contain /
410 $isFileHidden |= preg_match('/\\//', $name);
411 break;
412 }
413 $this->writelog($this->clientOS.' '.$name.' isHidden:'.$isFileHidden.' clientOS:'.$this->clientOS);
414 return $isFileHidden;
415 }
416
424 private function fileinfo($resourcePath, $displayPath, &$objDAV)
425 {
426 global $ilias;
427
428 $this->writelog('fileinfo('.$resourcePath.')');
429 // create result array
430 $info = array();
431 /* Some clients, for example WebDAV-Sync, need a trailing slash at the
432 * end of a resource path to a collection.
433 * However Mac OS X does not like this!
434 */
435 if ($objDAV->isCollection() && $this->clientOSFlavor != 'osx') {
436 $info['path'] = $resourcePath.'/';
437 } else {
438 $info['path'] = $resourcePath;
439 }
440
441 $info['props'] = array();
442
443 // no special beautified displayname here ...
444 $info["props"][] =& $this->mkprop("displayname", $displayPath);
445
446 // creation and modification time
447 $info["props"][] =& $this->mkprop("creationdate", $objDAV->getCreationTimestamp());
448 $info["props"][] =& $this->mkprop("getlastmodified", $objDAV->getModificationTimestamp());
449
450 // directory (WebDAV collection)
451 $info["props"][] =& $this->mkprop("resourcetype", $objDAV->getResourceType());
452 $info["props"][] =& $this->mkprop("getcontenttype", $objDAV->getContentType());
453 $info["props"][] =& $this->mkprop("getcontentlength", $objDAV->getContentLength());
454
455 // Only show supported locks for users who have write permission
456 if ($objDAV->isPermitted('write'))
457 {
458 $info["props"][] =& $this->mkprop("supportedlock",
459 '<D:lockentry>'
460 .'<D:lockscope><D:exclusive/></D:lockscope>'
461 .'<D:locktype><D:write/></D:locktype>'
462 .'</D:lockentry>'
463 .'<D:lockentry>'
464 .'<D:lockscope><D:shared/></D:lockscope>'
465 .'<D:locktype><D:write/></D:locktype>'
466 .'</D:lockentry>'
467 );
468 }
469
470 // Maybe we should only show locks on objects for users who have write permission.
471 // But if we don't show these locks, users who have write permission in an object
472 // further down in a hierarchy can't see who is locking their object.
473 $locks = $this->locks->getLocksOnObjectDAV($objDAV);
474 $lockdiscovery = '';
475 foreach ($locks as $lock)
476 {
477 // DAV Clients expects to see their own owner name in
478 // the locks. Since these names are not unique (they may
479 // just be the name of the local user running the DAV client)
480 // we return the ILIAS user name in all other cases.
481 if ($lock['ilias_owner'] == $ilias->account->getId())
482 {
483 $owner = $lock['dav_owner'];
484 } else {
485 $owner = '<D:href>'.$this->getLogin($lock['ilias_owner']).'</D:href>';
486 }
487 $this->writelog('lockowner='.$owner.' ibi:'.$lock['ilias_owner'].' davi:'.$lock['dav_owner']);
488
489 $lockdiscovery .=
490 '<D:activelock>'
491 .'<D:lockscope><D:'.$lock['scope'].'/></D:lockscope>'
492 //.'<D:locktype><D:'.$lock['type'].'/></D:locktype>'
493 .'<D:locktype><D:write/></D:locktype>'
494 .'<D:depth>'.$lock['depth'].'</D:depth>'
495 .'<D:owner>'.$owner.'</D:owner>'
496
497 // more than a million is considered an absolute timestamp
498 // less is more likely a relative value
499 .'<D:timeout>Second-'.(($lock['expires'] > 1000000) ? $lock['expires']-time():$lock['expires']).'</D:timeout>'
500 .'<D:locktoken><D:href>'.$lock['token'].'</D:href></D:locktoken>'
501 .'</D:activelock>'
502 ;
503 }
504 if (strlen($lockdiscovery) > 0)
505 {
506 $info["props"][] =& $this->mkprop("lockdiscovery", $lockdiscovery);
507 }
508
509 // get additional properties from database
510 $properties = $this->properties->getAll($objDAV);
511 foreach ($properties as $prop)
512 {
513 $info["props"][] = $this->mkprop($prop['namespace'], $prop['name'], $prop['value']);
514 }
515
516 //$this->writelog('fileinfo():'.var_export($info, true));
517 return $info;
518 }
519
531 public function GET(&$options)
532 {
533 global $ilUser;
534
535 $this->writelog('GET('.var_export($options, true).')');
536 $this->writelog('GET('.$options['path'].')');
537
538 // get dav object for path
539 $path = $this->davDeslashify($options['path']);
540 $objDAV =& $this->getObject($path);
541
542 // sanity check
543 if (is_null($objDAV) || $objDAV->isNullResource())
544 {
545 return false;
546 }
547
548 if (! $objDAV->isPermitted('visible,read'))
549 {
550 return '403 Forbidden';
551 }
552
553 // is this a collection?
554 if ($objDAV->isCollection())
555 {
556 if (isset($_GET['mount']))
557 {
558 return $this->mountDir($objDAV, $options);
559 }
560 else if (isset($_GET['mount-instructions']))
561 {
562 return $this->showMountInstructions($objDAV, $options);
563 }
564 else
565 {
566 return $this->getDir($objDAV, $options);
567 }
568 }
569 // detect content type
570 $options['mimetype'] =& $objDAV->getContentType();
571 // detect modification time
572 // see rfc2518, section 13.7
573 // some clients seem to treat this as a reverse rule
574 // requiring a Last-Modified header if the getlastmodified header was set
575 $options['mtime'] =& $objDAV->getModificationTimestamp();
576
577 // detect content length
578 $options['size'] =& $objDAV->getContentLength();
579
580 // get content as stream or as data array
581 $options['stream'] =& $objDAV->getContentStream();
582 if (is_null($options['stream']))
583 {
584 $options['data'] =& $objDAV->getContentData();
585 }
586
587 // Record read event and catch up write events
588 ilChangeEvent::_recordReadEvent($objDAV->getILIASType(), $objDAV->getRefId(),
589 $objDAV->getObjectId(), $ilUser->getId());
590
591 $this->writelog('GET:'.var_export($options, true));
592
593 return true;
594 }
606 private function mountDir(&$objDAV, &$options)
607 {
608 $path = $this->davDeslashify($options['path']);
609
610 header('Content-Type: application/davmount+xml');
611
612 echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
613 echo " </dm:url>".$this->base_uri."</dm:url>\n";
614
615 $xmlPath = str_replace('&','&amp;',$path);
616 $xmlPath = str_replace('<','&lt;',$xmlPath);
617 $xmlPath = str_replace('>','&gt;',$xmlPath);
618
619 echo " </dm:open>$xmlPath</dm:open>\n";
620 echo "</dm:mount>\n";
621
622 exit;
623
624 }
631 private function showMountInstructions(&$objDAV, &$options)
632 {
633 global $lng,$ilUser;
634
635 $path = $this->davDeslashify($options['path']);
636
637 // The $path variable may contain a full or a shortened DAV path.
638 // We convert it into an object path, which we can then use to
639 // construct a new full DAV path.
640 $objectPath = $this->toObjectPath($path);
641
642 // Construct a (possibly) full DAV path from the object path.
643 $fullPath = '';
644 foreach ($objectPath as $object)
645 {
646 if ($object->getRefId() == 1 && $this->isFileHidden($object))
647 {
648 // If the repository root object is hidden, we can not
649 // create a full path, because nothing would appear in the
650 // webfolder. We resort to a shortened path instead.
651 $fullPath .= '/ref_1';
652 }
653 else
654 {
655 $fullPath .= '/'.$this->davUrlEncode($object->getResourceName());
656 }
657 }
658
659 // Construct a shortened DAV path from the object path.
660 $shortenedPath = '/ref_'.
661 $objectPath[count($objectPath) - 1]->getRefId();
662
663 if ($objDAV->isCollection())
664 {
665 $shortenedPath .= '/';
666 $fullPath .= '/';
667 }
668
669 // Prepend client id to path
670 $shortenedPath = '/'.CLIENT_ID.$shortenedPath;
671 $fullPath = '/'.CLIENT_ID.$fullPath;
672
673 // Construct webfolder URI's. The URI's are used for mounting the
674 // webfolder. Since mounting using URI's is not standardized, we have
675 // to create different URI's for different browsers.
676 $webfolderURI = $this->base_uri.$shortenedPath;
677 $webfolderURI_Konqueror = ($this->isWebDAVoverHTTPS() ? "webdavs" : "webdav").
678 substr($this->base_uri, strrpos($this->base_uri,':')).
679 $shortenedPath;
680 ;
681 $webfolderURI_Nautilus = ($this->isWebDAVoverHTTPS() ? "davs" : "dav").
682 substr($this->base_uri, strrpos($this->base_uri,':')).
683 $shortenedPath
684 ;
685 $webfolderURI_IE = $this->base_uri.$shortenedPath;
686
687 $webfolderTitle = $objectPath[count($objectPath) - 1]->getResourceName();
688
689 header('Content-Type: text/html; charset=UTF-8');
690 echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
691 echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
692 echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
693 echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
694 echo " <head>\n";
695 echo " <title>".sprintf($lng->txt('webfolder_instructions_titletext'), $webfolderTitle)."</title>\n";
696 echo " </head>\n";
697 echo " <body>\n";
698
700 $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
701 $this->clientOS,$this->clientOSFlavor);
702
703 echo " </body>\n";
704 echo "</html>\n";
705
706 // Logout anonymous user to force authentication after calling mount uri
707 if($ilUser->getId() == ANONYMOUS_USER_ID)
708 {
709 $GLOBALS['ilAuth']->logout();
710 session_destroy();
711 }
712
713 exit;
714 }
724 private function getDir(&$objDAV, &$options)
725 {
726 global $ilias, $lng;
727
728 // Activate tree cache
729 global $tree;
730 //$tree->useCache(true);
731
732 $path = $this->davDeslashify($options['path']);
733
734 // The URL of a directory must end with a slash.
735 // If it does not we are redirecting the browser.
736 // The slash is required, because we are using relative links in the
737 // HTML code we are generating below.
738 if ($path.'/' != $options['path'])
739 {
740 header('Location: '.$this->base_uri.$path.'/');
741 exit;
742 }
743
744 header('Content-Type: text/html; charset=UTF-8');
745
746 // fixed width directory column format
747 $format = "%15s %-19s %-s\n";
748
749 echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
750 echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN\"\n";
751 echo " \"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd\">\n";
752 echo "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n";
753 echo "<head>\n";
754 echo "<title>".sprintf($lng->txt('webfolder_index_of'), $path)."</title>\n";
755
756 // Create "anchorClick" behavior for for Internet Explorer
757 // This allows to create a link to a webfolder
758 echo "<style type=\"text/css\">\n";
759 echo "<!--\n";
760 echo "a {\n";
761 echo " behavior:url(#default#AnchorClick);\n";
762 echo "}\n";
763 echo "-->\n";
764 echo "</style>\n";
765
766 echo "</head><body>\n";
767
768 $hrefPath = '';
769 $pathComponents = explode('/',$path);
770 $uriComponents = array();
771 foreach ($pathComponents as $component)
772 {
773 $uriComponents[] = $this->davUrlEncode($component);
774 }
775 for ($i = 0; $i < count($pathComponents); $i++)
776 {
777 $displayName = htmlspecialchars($pathComponents[$i]);
778 if ($i != 0) {
779 $hrefPath .= '/';
780 }
781 $uriPath = implode('/', array_slice($uriComponents,0,$i + 1));
782 if ($i < 2)
783 {
784 // The first two path elements consist of the webdav.php script
785 // and the client id. These elements are not part of the
786 // directory structure and thus are not represented as links.
787 $hrefPath .= $displayName;
788 }
789 else
790 {
791 $hrefPath .= '<a href="'.$this->base_uri.$uriPath.'/">'.$displayName.'</a>';
792 }
793 }
794 echo "<h3>".sprintf($lng->txt('webfolder_index_of'), $hrefPath)."</h3>\n";
795
796 // Display user id
797 if ($ilias->account->getLogin() == 'anonymous')
798 {
799 echo "<p><font size=\"-1\">".$lng->txt('not_logged_in')."</font><br>\n";
800 } else {
801 echo "<p><font size=\"-1\">".$lng->txt('login_as')." <i>"
802 .$ilias->account->getFirstname().' '
803 .$ilias->account->getLastname().' '
804 .' '.$ilias->account->getLogin().'</i> '
805 .', '.$lng->txt('client').' <i>'.$ilias->getClientId().'</i>.'
806 ."</font><p>\n";
807 }
808
809 // Create "open as webfolder" link
810 $href = $this->base_uri.$uriPath;
811 // IE can not mount long paths. If the path has more than one element, we
812 // create a relative path with a ref-id.
813 if (count($pathComponents) > 2)
814 {
815 $hrefIE = $this->base_uri.'/'.CLIENT_ID.'/ref_'.$objDAV->getRefId();
816 } else {
817 $hrefIE = $href;
818 }
819 echo "<p><font size=\"-1\">".
820 sprintf($lng->txt('webfolder_dir_info'), "$href?mount-instructions").
821 "</font></p>\n";
822 echo "<p><font size=\"-1\">".
823 sprintf($lng->txt('webfolder_mount_dir_with'),
824 "$hrefIE\" folder=\"$hrefIE", // Internet Explorer
825 'webdav'.substr($href,4), // Konqueror
826 'dav'.substr($href,4), // Nautilus
827 $href.'?mount' // RFC 4709
828 )
829 ."</font></p>\n";
830
831 echo "<pre>";
832 printf($format, $lng->txt('size'), $lng->txt('last_change'), $lng->txt('filename'));
833 echo "<hr>";
834
835 $collectionCount = 0;
836 $fileCount = 0;
837 $children =& $objDAV->childrenWithPermission('visible,read');
838 foreach ($children as $childDAV) {
839 if ($childDAV->isCollection() && !$this->isFileHidden($childDAV))
840 {
841 $collectionCount++;
842 $name = $this->davUrlEncode($childDAV->getResourceName());
843 printf($format,
844 '-',
845 strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
846 '<a href="'.$name.'/'.'">'.$childDAV->getDisplayName()."</a>");
847 }
848 }
849 foreach ($children as $childDAV) {
850 if ($childDAV->isFile() && !$this->isFileHidden($childDAV))
851 {
852 $fileCount++;
853 $name = $this->davUrlEncode($childDAV->getResourceName());
854 printf($format,
855 number_format($childDAV->getContentLength()),
856 strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
857 '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
858 }
859 }
860 foreach ($children as $childDAV) {
861 if ($childDAV->isNullResource() && !$this->isFileHidden($childDAV))
862 {
863 $name = $this->davUrlEncode($childDAV->getResourceName());
864 printf($format,
865 'Lock',
866 strftime("%Y-%m-%d %H:%M:%S", $childDAV->getModificationTimestamp()),
867 '<a href="'.$name.'">'.$childDAV->getDisplayName()."</a>");
868 }
869 }
870 echo "<hr>";
871 echo $collectionCount.' '.$lng->txt(($collectionCount == 1) ? 'folder' : 'folders').', ';
872 echo $fileCount.' '.$lng->txt(($fileCount == 1) ? 'file' : 'files').'.';
873 echo "</pre>";
874 echo "</body></html>\n";
875
876 exit;
877 }
878
879
886 public function PUT(&$options)
887 {
888 global $ilUser;
889
890 $this->writelog('PUT('.var_export($options, true).')');
891
892 $path = $this->davDeslashify($options['path']);
893 $parent = dirname($path);
894 $name = $this->davBasename($path);
895
896 //Check if FileType is allowed
897 if ($name != ilFileUtils::getValidFilename($name)) {
898 return false;
899 }
900
901 // get dav object for path
902 $parentDAV =& $this->getObject($parent);
903
904 // sanity check
905 if (is_null($parentDAV) || ! $parentDAV->isCollection()) {
906 return '409 Conflict';
907 }
908
909 // Prevent putting of files which exceed upload limit
910 // FIXME: since this is an optional parameter, we should to do the
911 // same check again in function PUTfinished.
912 if ($options['content_length'] != null &&
913 $options['content_length'] > $this->getUploadMaxFilesize()) {
914
915 $this->writelog('PUT is forbidden, because content length='.
916 $options['content_length'].' is larger than upload_max_filesize='.
917 $this->getUploadMaxFilesize().'in php.ini');
918
919 return '403 Forbidden';
920 }
921
922 // determine mime type
923 include_once("./Services/Utilities/classes/class.ilMimeTypeUtil.php");
924 $mime = ilMimeTypeUtil::lookupMimeType($name);
925
926 $objDAV =& $this->getObject($path);
927 if (is_null($objDAV))
928 {
929 $ttype = $parentDAV->getILIASFileType();
930 $isperm = $parentDAV->isPermitted('create', $ttype);
931 if (! $isperm)
932 {
933 $this->writelog('PUT is forbidden, because user has no create permission');
934
935 return '403 Forbidden';
936 }
937 $options["new"] = true;
938 $objDAV =& $parentDAV->createFile($name);
939 $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
940 //$objDAV->setContentType($options['content_type']);
941 $objDAV->setContentType($mime);
942 if ($options['content_length'] != null)
943 {
944 $objDAV->setContentLength($options['content_length']);
945 }
946 $objDAV->write();
947 // Record write event
948 ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
949 }
950 else if ($objDAV->isNullResource())
951 {
952 if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()))
953 {
954 $this->writelog('PUT is forbidden, because user has no create permission');
955 return '403 Forbidden';
956 }
957 $options["new"] = false;
958 $objDAV =& $parentDAV->createFileFromNull($name, $objDAV);
959 $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type']);
960 //$objDAV->setContentType($options['content_type']);
961 $objDAV->setContentType($mime);
962 if ($options['content_length'] != null)
963 {
964 $objDAV->setContentLength($options['content_length']);
965 }
966 $objDAV->write();
967
968 // Record write event
969 ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
970 }
971 else
972 {
973 if (! $objDAV->isPermitted('write'))
974 {
975 $this->writelog('PUT is forbidden, because user has no write permission');
976 return '403 Forbidden';
977 }
978 $options["new"] = false;
979 $this->writelog('PUT obj='.$objDAV.' name='.$name.' content_type='.$options['content_type'].' content_length='.$options['content_length']);
980
981 // Create a new version if the previous version is not empty
982 if ($objDAV->getContentLength() != 0) {
983 $objDAV->createNewVersion();
984 }
985
986 //$objDAV->setContentType($options['content_type']);
987 $objDAV->setContentType($mime);
988 if ($options['content_length'] != null)
989 {
990 $objDAV->setContentLength($options['content_length']);
991 }
992 $objDAV->write();
993
994 // Record write event
995 ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'update');
996 ilChangeEvent::_catchupWriteEvents($objDAV->getObjectId(), $ilUser->getId(), 'update');
997 }
998 // store this object, we reuse it in method PUTfinished
999 $this->putObjDAV = $objDAV;
1000
1001 $out =& $objDAV->getContentOutputStream();
1002 $this->writelog('PUT outputstream='.$out);
1003
1004 return $out;
1005 }
1006
1013 public function PUTfinished(&$options)
1014 {
1015 $this->writelog('PUTfinished('.var_export($options, true).')');
1016
1017 if($this->putObjDAV->getResourceType()==""){
1018 $vir = ilUtil::virusHandling($this->putObjDAV->obj->getDirectory($this->putObjDAV->obj->version).'/'.$this->putObjDAV->obj->filename, $this->putObjDAV->obj->filename);
1019 if ($vir[0] == false)
1020 {
1021 $this->writelog('PUTfinished Virus found: '.$vir[1]);
1022 //delete file
1024 return false;
1025 }
1026 }
1027
1028 // Update the content length in the file object, if the
1029 // the client did not specify a content_length
1030 if ($options['content_length'] == null || $this->putObjDAV->getContentLength() == 0)
1031 {
1032 $objDAV = $this->putObjDAV;
1033 if ($objDAV->getContentOutputStreamLength() != null) {
1034 $objDAV->setContentLength($objDAV->getContentOutputStreamLength());
1035 } else {
1036 $objDAV->write();
1037 $objDAV->setContentLength(filesize($objDAV->obj->getDirectory($objDAV->obj->version).'/'.$objDAV->obj->filename));
1038 }
1039 $objDAV->write();
1040 $this->putObjDAV = null;
1041 }
1042 return true;
1043 }
1044
1045
1052 public function MKCOL($options)
1053 {
1054 global $ilUser;
1055
1056 $this->writelog('MKCOL('.var_export($options, true).')');
1057 $this->writelog('MKCOL '.$options['path']);
1058
1059 $path =& $this->davDeslashify($options['path']);
1060 $parent =& dirname($path);
1061 $name =& $this->davBasename($path);
1062
1063 // No body parsing yet
1064 if(!empty($_SERVER["CONTENT_LENGTH"])) {
1065 return "415 Unsupported media type";
1066 }
1067
1068 // Check if an object with the path already exists.
1069 $objDAV =& $this->getObject($path);
1070 if (! is_null($objDAV))
1071 {
1072 return '405 Method not allowed';
1073 }
1074
1075 // get parent dav object for path
1076 $parentDAV =& $this->getObject($parent);
1077
1078 // sanity check
1079 if (is_null($parentDAV) || ! $parentDAV->isCollection())
1080 {
1081 return '409 Conflict';
1082 }
1083
1084 if (! $parentDAV->isPermitted('create',$parentDAV->getILIASCollectionType()))
1085 {
1086 return '403 Forbidden';
1087 }
1088
1089 // XXX Implement code that Handles null resource here
1090
1091 $objDAV = $parentDAV->createCollection($name);
1092
1093 if ($objDAV != null)
1094 {
1095 // Record write event
1096 ilChangeEvent::_recordWriteEvent((int) $objDAV->getObjectId(), $ilUser->getId(), 'create', $parentDAV->getObjectId());
1097 }
1098
1099 $result = ($objDAV != null) ? "201 Created" : "409 Conflict";
1100 return $result;
1101 }
1102
1103
1110 public function DELETE($options)
1111 {
1112 global $ilUser;
1113
1114 $this->writelog('DELETE('.var_export($options, true).')');
1115 $this->writelog('DELETE '.$options['path']);
1116
1117 // get dav object for path
1118 $path =& $this->davDeslashify($options['path']);
1119 $parentDAV =& $this->getObject(dirname($path));
1120 $objDAV =& $this->getObject($path);
1121
1122 // sanity check
1123 if (is_null($objDAV) || $objDAV->isNullResource())
1124 {
1125 return '404 Not Found';
1126 }
1127 if (! $objDAV->isPermitted('delete'))
1128 {
1129 return '403 Forbidden';
1130 }
1131
1132 $parentDAV->remove($objDAV);
1133
1134 // Record write event
1135 ilChangeEvent::_recordWriteEvent($objDAV->getObjectId(), $ilUser->getId(), 'delete', $parentDAV->getObjectId());
1136
1137 return '204 No Content';
1138 }
1139
1146 public function MOVE($options)
1147 {
1148 global $ilUser;
1149
1150 $this->writelog('MOVE('.var_export($options, true).')');
1151 $this->writelog('MOVE '.$options['path'].' '.$options['dest']);
1152
1153 // Get path names
1154 $src = $this->davDeslashify($options['path']);
1155 $srcParent = dirname($src);
1156 $srcName = $this->davBasename($src);
1157 $dst = $this->davDeslashify($options['dest']);
1158
1159 $dstParent = dirname($dst);
1160 $dstName = $this->davBasename($dst);
1161 $this->writelog('move '.$dst.' dstname='.$dstName);
1162 // Source and destination must not be the same
1163 if ($src == $dst)
1164 {
1165 return '409 Conflict (source and destination are the same)';
1166 }
1167
1168 // Destination must not be in a subtree of source
1169 if (substr($dst,strlen($src)+1) == $src.'/')
1170 {
1171 return '409 Conflict (destination is in subtree of source)';
1172 }
1173
1174 // Get dav objects for path
1175 $srcDAV =& $this->getObject($src);
1176 $dstDAV =& $this->getObject($dst);
1177 $srcParentDAV =& $this->getObject($srcParent);
1178 $dstParentDAV =& $this->getObject($dstParent);
1179
1180 // Source must exist
1181 if ($srcDAV == null)
1182 {
1183 return '409 Conflict (source does not exist)';
1184 }
1185
1186 // Overwriting is only allowed, if overwrite option is set to 'T'
1187 $isOverwritten = false;
1188 if ($dstDAV != null)
1189 {
1190 if ($options['overwrite'] == 'T')
1191 {
1192 // Delete the overwritten destination
1193 if ($dstDAV->isPermitted('delete'))
1194 {
1195 $dstParentDAV->remove($dstDAV);
1196 $dstDAV = null;
1197 $isOverwritten = true;
1198 } else {
1199 return '403 Not Permitted';
1200 }
1201 } else {
1202 return '412 Precondition Failed';
1203 }
1204 }
1205
1206 // Parents of destination must exist
1207 if ($dstParentDAV == null)
1208 {
1209 return '409 Conflict (parent of destination does not exist)';
1210 }
1211
1212 if ($srcParent == $dstParent)
1213 {
1214 // Rename source, if source and dest are in same parent
1215
1216 // Check permission
1217 if (! $srcDAV->isPermitted('write'))
1218 {
1219 return '403 Forbidden';
1220 }
1221 $this->writelog('rename dstName='.$dstName);
1222 $srcDAV->setResourceName($dstName);
1223 $srcDAV->write();
1224 } else {
1225 // Move source, if source and dest are in same parent
1226
1227
1228 if (! $srcDAV->isPermitted('delete'))
1229 {
1230 return '403 Forbidden';
1231 }
1232
1233 if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1234 {
1235 return '403 Forbidden';
1236 }
1237 $dstParentDAV->addMove($srcDAV, $dstName);
1238 }
1239
1240 // Record write event
1241 if ($isOverwritten)
1242 {
1243 ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'rename');
1244 }
1245 else
1246 {
1247 ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'remove', $srcParentDAV->getObjectId());
1248 ilChangeEvent::_recordWriteEvent($srcDAV->getObjectId(), $ilUser->getId(), 'add', $dstParentDAV->getObjectId());
1249 }
1250
1251 return ($isOverwritten) ? '204 No Content' : '201 Created';
1252 }
1253
1260 public function COPY($options, $del=false)
1261 {
1262 global $ilUser;
1263 $this->writelog('COPY('.var_export($options, true).' ,del='.$del.')');
1264 $this->writelog('COPY '.$options['path'].' '.$options['dest']);
1265
1266 // no copying to different WebDAV Servers
1267 if (isset($options["dest_url"])) {
1268 return "502 bad gateway";
1269 }
1270
1271 $src = $this->davDeslashify($options['path']);
1272 $srcParent = dirname($src);
1273 $srcName = $this->davBasename($src);
1274 $dst = $this->davDeslashify($options['dest']);
1275 $dstParent = dirname($dst);
1276 $dstName = $this->davBasename($dst);
1277
1278 // sanity check
1279 if ($src == $dst)
1280 {
1281 return '409 Conflict'; // src and dst are the same
1282 }
1283
1284 if (substr($dst,strlen($src)+1) == $src.'/')
1285 {
1286 return '409 Conflict'; // dst is in subtree of src
1287 }
1288
1289 $this->writelog('COPY src='.$src.' dst='.$dst);
1290 // get dav object for path
1291 $srcDAV =& $this->getObject($src);
1292 $dstDAV =& $this->getObject($dst);
1293 $dstParentDAV =& $this->getObject($dstParent);
1294
1295 if (is_null($srcDAV) || $srcDAV->isNullResource())
1296 {
1297 return '409 Conflict'; // src does not exist
1298 }
1299 if (is_null($dstParentDAV) || $dstParentDAV->isNullResource())
1300 {
1301 return '409 Conflict'; // parent of dst does not exist
1302 }
1303 $isOverwritten = false;
1304
1305 // XXX Handle nulltype for dstDAV
1306 if (! is_null($dstDAV))
1307 {
1308 if ($options['overwrite'] == 'T')
1309 {
1310 if ($dstDAV->isPermitted('delete'))
1311 {
1312 $dstParentDAV->remove($dstDAV);
1313 ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'delete', $dstParentDAV->getObjectId());
1314
1315 $dstDAV = null;
1316 $isOverwritten = true;
1317 } else {
1318 return '403 Forbidden';
1319 }
1320 } else {
1321 return '412 Precondition Failed';
1322 }
1323 }
1324
1325 if (! $dstParentDAV->isPermitted('create', $srcDAV->getILIASType()))
1326 {
1327 return '403 Forbidden';
1328 }
1329 $dstDAV = $dstParentDAV->addCopy($srcDAV, $dstName);
1330
1331 // Record write event
1332 ilChangeEvent::_recordReadEvent($srcDAV->getILIASType(), $srcDAV->getRefId(),
1333 $srcDAV->getObjectId(), $ilUser->getId());
1334 ilChangeEvent::_recordWriteEvent($dstDAV->getObjectId(), $ilUser->getId(), 'create', $dstParentDAV->getObjectId());
1335
1336 return ($isOverwritten) ? '204 No Content' : '201 Created';
1337 }
1338
1345 public function PROPPATCH(&$options)
1346 {
1347 $this->writelog('PROPPATCH(options='.var_export($options, true).')');
1348 $this->writelog('PROPPATCH '.$options['path']);
1349
1350 // get dav object for path
1351 $path =& $this->davDeslashify($options['path']);
1352 $objDAV =& $this->getObject($path);
1353
1354 // sanity check
1355 if (is_null($objDAV) || $objDAV->isNullResource()) return false;
1356
1357 $isPermitted = $objDAV->isPermitted('write');
1358 foreach($options['props'] as $key => $prop) {
1359 if (!$isPermitted || $prop['ns'] == 'DAV:')
1360 {
1361 $options['props'][$key]['status'] = '403 Forbidden';
1362 } else {
1363 $this->properties->put($objDAV, $prop['ns'],$prop['name'],$prop['val']);
1364 }
1365 }
1366
1367 return "";
1368 }
1369
1370
1377 public function LOCK(&$options)
1378 {
1379 global $ilias;
1380 $this->writelog('LOCK('.var_export($options, true).')');
1381 $this->writelog('LOCK '.$options['path']);
1382
1383 // Check if an object with the path exists.
1384 $path =& $this->davDeslashify($options['path']);
1385 $objDAV =& $this->getObject($path);
1386 // Handle null-object locking
1387 // --------------------------
1388 if (is_null($objDAV))
1389 {
1390 $this->writelog('LOCK handling null-object locking...');
1391
1392 // If the name does not exist, we create a null-object for it
1393 if (isset($options["update"]))
1394 {
1395 $this->writelog('LOCK lock-update failed on non-existing null-object.');
1396 return '412 Precondition Failed';
1397 }
1398
1399 $parent = dirname($path);
1400 $parentDAV =& $this->getObject($parent);
1401 if (is_null($parentDAV))
1402 {
1403 $this->writelog('LOCK lock failed on non-existing path to null-object.');
1404 return '404 Not Found';
1405 }
1406 if (! $parentDAV->isPermitted('create', $parentDAV->getILIASFileType()) &&
1407 ! $parentDAV->isPermitted('create', $parentDAV->getILIASCollectionType()))
1408 {
1409 $this->writelog('LOCK lock failed - creation of null object not permitted.');
1410 return '403 Forbidden';
1411 }
1412
1413 $objDAV =& $parentDAV->createNull($this->davBasename($path));
1414 $this->writelog('created null resource for '.$path);
1415 }
1416
1417 // ---------------------
1418 if (! $objDAV->isNullResource() && ! $objDAV->isPermitted('write'))
1419 {
1420 $this->writelog('LOCK lock failed - user has no write permission.');
1421 return '403 Forbidden';
1422 }
1423
1424 // XXX - Check if there are other locks on the resource
1425 if (!isset($options['timeout']) || is_array($options['timeout']))
1426 {
1427 $options["timeout"] = time()+360; // 6min.
1428 }
1429
1430 if(isset($options["update"])) { // Lock Update
1431 $this->writelog('LOCK update token='.var_export($options,true));
1432 $success = $this->locks->updateLockWithoutCheckingDAV(
1433 $objDAV,
1434 $options['update'],
1435 $options['timeout']
1436 );
1437 if ($success)
1438 {
1439 $data = $this->locks->getLockDAV($objDAV, $options['update']);
1440 if ($data['ilias_owner'] == $ilias->account->getId())
1441 {
1442 $owner = $data['dav_owner'];
1443 } else {
1444 $owner = '<D:href>'.$this->getLogin($data['ilias_owner']).'</D:href>';
1445 }
1446 $options['owner'] = $owner;
1447 $options['locktoken'] = $data['token'];
1448 $options['timeout'] = $data['expires'];
1449 $options['depth'] = $data['depth'];
1450 $options['scope'] = $data['scope'];
1451 $options['type'] = $data['scope'];
1452 }
1453
1454 } else {
1455 $this->writelog('LOCK create new lock');
1456
1457 // XXX - Attempting to create a recursive exclusive lock
1458 // on a collection must fail, if any of nodes in the subtree
1459 // of the collection already has a lock.
1460 // XXX - Attempting to create a recursive shared lock
1461 // on a collection must fail, if any of nodes in the subtree
1462 // of the collection already has an exclusive lock.
1463 //$owner = (strlen(trim($options['owner'])) == 0) ? $ilias->account->getLogin() : $options['owner'];
1464 $this->writelog('lock owner='.$owner);
1465 $success = $this->locks->lockWithoutCheckingDAV(
1466 $objDAV,
1467 $ilias->account->getId(),
1468 trim($options['owner']),
1469 $options['locktoken'],
1470 $options['timeout'],
1471 $options['depth'],
1472 $options['scope']
1473 );
1474 }
1475
1476 // Note: As a workaround for the Microsoft WebDAV Client, we return
1477 // true/false here (resulting in the status '200 OK') instead of
1478 // '204 No Content').
1479 //return ($success) ? '204 No Content' : false;
1480 return $success;
1481 }
1482
1489 public function UNLOCK(&$options)
1490 {
1491 global $log, $ilias;
1492 $this->writelog('UNLOCK(options='.var_export($options, true).')');
1493 $this->writelog('UNLOCK '.$options['path']);
1494
1495 // Check if an object with the path exists.
1496 $path =& $this->davDeslashify($options['path']);
1497 $objDAV =& $this->getObject($path);
1498 if (is_null($objDAV)) {
1499 return '404 Not Found';
1500 }
1501 if (! $objDAV->isPermitted('write')) {
1502 return '403 Forbidden';
1503 }
1504
1505 $success = $this->locks->unlockWithoutCheckingDAV(
1506 $objDAV,
1507 $options['token']
1508 );
1509
1510 // Delete null resource object if there are no locks associated to
1511 // it anymore
1512 if ($objDAV->isNullResource()
1513 && count($this->locks->getLocksOnObjectDAV($objDAV)) == 0)
1514 {
1515 $parent = dirname($this->davDeslashify($options['path']));
1516 $parentDAV =& $this->getObject($parent);
1517 $parentDAV->remove($objDAV);
1518 }
1519
1520 // Workaround for Mac OS X: We must return 200 here instead of
1521 // 204.
1522 //return ($success) ? '204 No Content' : '412 Precondition Failed';
1523 return ($success) ? '200 OK' : '412 Precondition Failed';
1524 }
1525
1538 protected function checkLock($path)
1539 {
1540 global $ilias;
1541
1542 $this->writelog('checkLock('.$path.')');
1543 $result = null;
1544
1545 // get dav object for path
1546 //$objDAV = $this->getObject($path);
1547
1548 // convert DAV path into ilObjectDAV path
1549 $objPath = $this->toObjectPath($path);
1550 if (! is_null($objPath))
1551 {
1552 $objDAV = $objPath[count($objPath) - 1];
1553 $locks = $this->locks->getLocksOnPathDAV($objPath);
1554 foreach ($locks as $lock)
1555 {
1556 $isLastPathComponent = $lock['obj_id'] == $objDAV->getObjectId()
1557 && $lock['node_id'] == $objDAV->getNodeId();
1558
1559 // Check all locks on last object in path,
1560 // but only locks with depth infinity on parent objects.
1561 if ($isLastPathComponent || $lock['depth'] == 'infinity')
1562 {
1563 // DAV Clients expects to see their own owner name in
1564 // the locks. Since these names are not unique (they may
1565 // just be the name of the local user running the DAV client)
1566 // we return the ILIAS user name in all other cases.
1567 if ($lock['ilias_owner'] == $ilias->account->getId())
1568 {
1569 $owner = $lock['dav_owner'];
1570 } else {
1571 $owner = $this->getLogin($lock['ilias_owner']);
1572 }
1573
1574 // FIXME - Shouldn't we collect all locks instead of
1575 // using an arbitrary one?
1576 $result = array(
1577 "type" => "write",
1578 "obj_id" => $lock['obj_id'],
1579 "node_id" => $lock['node_id'],
1580 "scope" => $lock['scope'],
1581 "depth" => $lock['depth'],
1582 "owner" => $owner,
1583 "token" => $lock['token'],
1584 "expires" => $lock['expires']
1585 );
1586 if ($lock['scope'] == 'exclusive')
1587 {
1588 // If there is an exclusive lock in the path, it
1589 // takes precedence over all non-exclusive locks in
1590 // parent nodes. Therefore we can can finish collecting
1591 // locks.
1592 break;
1593 }
1594 }
1595 }
1596 }
1597 $this->writelog('checkLock('.$path.'):'.var_export($result,true));
1598
1599 return $result;
1600 }
1601
1606 protected function getLogin($userId)
1607 {
1608 $login = ilObjUser::_lookupLogin($userId);
1609 $this->writelog('getLogin('.$userId.'):'.var_export($login,true));
1610 return $login;
1611 }
1612
1613
1620 private function getObject($davPath)
1621 {
1622 global $tree;
1623
1624
1625 // If the second path elements starts with 'file_', the following
1626 // characters of the path element directly identify the ref_id of
1627 // a file object.
1628 $davPathComponents = split('/',substr($davPath,1));
1629 if (count($davPathComponents) > 1 &&
1630 substr($davPathComponents[1],0,5) == 'file_')
1631 {
1632 $ref_id = substr($davPathComponents[1],5);
1633 $nodePath = $tree->getNodePath($ref_id, $tree->root_id);
1634
1635 // Poor IE needs this, in order to successfully display
1636 // PDF documents
1637 header('Pragma: private');
1638 }
1639 else
1640 {
1641 $nodePath = $this->toNodePath($davPath);
1642 if ($nodePath == null && count($davPathComponents) == 1)
1643 {
1644 return ilObjectDAV::createObject(-1,'mountPoint');
1645 }
1646 }
1647 if (is_null($nodePath))
1648 {
1649 return null;
1650 } else {
1651 $top = $nodePath[count($nodePath) - 1];
1652 return ilObjectDAV::createObject($top['child'],$top['type']);
1653 }
1654 }
1661 private function toObjectPath($davPath)
1662 {
1663 $this->writelog('toObjectPath('.$davPath);
1664 global $tree;
1665
1666 $nodePath = $this->toNodePath($davPath);
1667
1668 if (is_null($nodePath))
1669 {
1670 return null;
1671 } else {
1672 $objectPath = array();
1673 foreach ($nodePath as $node)
1674 {
1675 $pathElement = ilObjectDAV::createObject($node['child'],$node['type']);
1676 if (is_null($pathElement))
1677 {
1678 break;
1679 }
1680 $objectPath[] = $pathElement;
1681 }
1682 return $objectPath;
1683 }
1684 }
1685
1697 public function toNodePath($davPath)
1698 {
1699 global $tree;
1700 $this->writelog('toNodePath('.$davPath.')...');
1701
1702 // Split the davPath into path titles
1703 $titlePath = split('/',substr($davPath,1));
1704
1705 // Remove the client id from the beginning of the title path
1706 if (count($titlePath) > 0)
1707 {
1708 array_shift($titlePath);
1709 }
1710
1711 // If the last path title is empty, remove it
1712 if (count($titlePath) > 0 && $titlePath[count($titlePath) - 1] == '')
1713 {
1714 array_pop($titlePath);
1715 }
1716
1717 // If the path is empty, return null
1718 if (count($titlePath) == 0)
1719 {
1720 $this->writelog('toNodePath('.$davPath.'):null, because path is empty.');
1721 return null;
1722 }
1723
1724 // If the path is an absolute path, ref_id is null.
1725 $ref_id = null;
1726
1727 // If the path is a relative folder path, convert it into an absolute path
1728 if (count($titlePath) > 0 && substr($titlePath[0],0,4) == 'ref_')
1729 {
1730 $ref_id = substr($titlePath[0],4);
1731 array_shift($titlePath);
1732 }
1733
1734 $nodePath = $tree->getNodePathForTitlePath($titlePath, $ref_id);
1735
1736 $this->writelog('toNodePath():'.var_export($nodePath,true));
1737 return $nodePath;
1738 }
1739
1746 private function davDeslashify($path)
1747 {
1749
1750 if ($path[strlen($path)-1] == '/') {
1751 $path = substr($path,0, strlen($path) - 1);
1752 }
1753 $this->writelog('davDeslashify:'.$path);
1754 return $path;
1755 }
1756
1763 private function davBasename($path)
1764 {
1765 $components = split('/',$path);
1766 return count($components) == 0 ? '' : $components[count($components) - 1];
1767 }
1768
1775 protected function writelog($message)
1776 {
1777 // Only write log message, if we are in debug mode
1778 if ($this->isDebug)
1779 {
1780 global $ilLog, $ilias;
1781 if ($ilLog)
1782 {
1783 if ($message == '---')
1784 {
1785 $ilLog->write('');
1786 } else {
1787 $ilLog->write(
1788 $ilias->account->getLogin()
1789 .' '.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT']
1790 .' ilDAVServer.'.str_replace("\n",";",$message)
1791 );
1792 }
1793 }
1794 else
1795 {
1796 $fh = fopen('/opt/ilias/log/ilias.log', 'a');
1797 fwrite($fh, date('Y-m-d H:i:s '));
1798 fwrite($fh, str_replace("\n",";",$message));
1799 fwrite($fh, "\n\n");
1800 fclose($fh);
1801 }
1802 }
1803 }
1804
1817 function getMountURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null, $genericURI = false)
1818 {
1819 if ($genericURI) {
1820 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1821 $query = null;
1822 } else if ($this->clientOS == 'windows') {
1823 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1824 $query = 'mount-instructions';
1825 } else if ($this->clientBrowser == 'konqueror') {
1826 $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1827 $query = null;
1828 } else if ($this->clientBrowser == 'nautilus') {
1829 $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1830 $query = null;
1831 } else {
1832 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1833 $query = 'mount-instructions';
1834 }
1835 $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1836 $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1837
1838 $uri = $baseUri.'/ref_'.$refId.'/';
1839 if ($query != null)
1840 {
1841 $uri .= '?'.$query;
1842 }
1843
1844 return $uri;
1845 }
1860 function getFolderURI($refId, $nodeId = 0, $ressourceName = null, $parentRefId = null)
1861 {
1862 if ($this->clientOS == 'windows') {
1863 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1864 $query = null;
1865 } else if ($this->clientBrowser == 'konqueror') {
1866 $baseUri = ($this->isWebDAVoverHTTPS() ? "webdavs:" : "webdav:");
1867 $query = null;
1868 } else if ($this->clientBrowser == 'nautilus') {
1869 $baseUri = ($this->isWebDAVoverHTTPS() ? "davs:" : "dav:");
1870 $query = null;
1871 } else {
1872 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:");
1873 $query = null;
1874 }
1875 $baseUri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1876 $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1877
1878 $uri = $baseUri.'/ref_'.$refId.'/';
1879 if ($query != null)
1880 {
1881 $uri .= '?'.$query;
1882 }
1883
1884 return $uri;
1885 }
1897 public function getObjectURI($refId, $ressourceName = null, $parentRefId = null)
1898 {
1899 $nodeId = 0;
1900 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1901 "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1902 $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1903
1904 if (! is_null($ressourceName) && ! is_null($parentRefId))
1905 {
1906 // Quickly create URI from the known data without needing SQL queries
1907 $uri = $baseUri.'/ref_'.$parentRefId.'/'.$this->davUrlEncode($ressourceName);
1908 } else {
1909 // Create URI and use some SQL queries to get the missing data
1910 global $tree;
1911 $nodePath = $tree->getNodePath($refId);
1912
1913 if (is_null($nodePath) || count($nodePath) < 2)
1914 {
1915 // No object path? Return null - file is not in repository.
1916 $uri = null;
1917 } else {
1918 $uri = $baseUri.'/ref_'.$nodePath[count($nodePath) - 2]['child'].'/'.
1919 $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1920 }
1921 }
1922 return $uri;
1923 }
1924
1942 public function getFileURI($refId, $ressourceName = null, $parentRefId = null)
1943 {
1944 $nodeId = 0;
1945 $baseUri = ($this->isWebDAVoverHTTPS() ? "https:" : "http:").
1946 "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
1947 $baseUri = substr($baseUri,0,strrpos($baseUri,'/')).'/webdav.php/'.CLIENT_ID;
1948
1949 if (! is_null($ressourceName) && ! is_null($parentRefId))
1950 {
1951 // Quickly create URI from the known data without needing SQL queries
1952 $uri = $baseUri.'/file_'.$refId.'/'.$this->davUrlEncode($ressourceName);
1953 } else {
1954 // Create URI and use some SQL queries to get the missing data
1955 global $tree;
1956 $nodePath = $tree->getNodePath($refId);
1957
1958 if (is_null($nodePath) || count($nodePath) < 2)
1959 {
1960 // No object path? Return null - file is not in repository.
1961 $uri = null;
1962 } else {
1963 $uri = $baseUri.'/file_'.$nodePath[count($nodePath) - 1]['child'].'/'.
1964 $this->davUrlEncode($nodePath[count($nodePath) - 1]['title']);
1965 }
1966 }
1967 return $uri;
1968 }
1969
1975 public function isWebDAVoverHTTPS() {
1976 if ($this->isHTTPS == null) {
1977 global $ilSetting;
1978 require_once './Services/Http/classes/class.ilHTTPS.php';
1979 $https = new ilHTTPS();
1980 $this->isHTTPS = $https->isDetected() || $ilSetting->get('https');
1981 }
1982 return $this->isHTTPS;
1983 }
1984
1993 public static function _isActive()
1994 {
1995 global $ilClientIniFile;
1996 return $ilClientIniFile->readVariable('file_access','webdav_enabled') == '1' &&
1997 @include_once("Auth/HTTP.php");
1998 }
2004 public static function _isActionsVisible()
2005 {
2006 global $ilClientIniFile;
2007 return $ilClientIniFile->readVariable('file_access','webdav_actions_visible') == '1';
2008 }
2009
2019 public static function _getDefaultWebfolderInstructions()
2020 {
2021 global $lng;
2022 return $lng->txt('webfolder_instructions_text');
2023 }
2024
2051 public static function _getWebfolderInstructionsFor($webfolderTitle,
2052 $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus,
2053 $os = 'unknown', $osFlavor = 'unknown')
2054 {
2055 global $ilSetting;
2056
2057 $settings = new ilSetting('file_access');
2058 $str = $settings->get('custom_webfolder_instructions', '');
2059 if (strlen($str) == 0 || ! $settings->get('custom_webfolder_instructions_enabled'))
2060 {
2062 }
2063 if(is_file('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm')){
2064 $str = fread(fopen('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm', "rb"),filesize('Customizing/clients/'.CLIENT_ID.'/webdavtemplate.htm'));
2065 }
2066 $str=utf8_encode($str);
2067
2068 preg_match_all('/(\\d+)/', $webfolderURI, $matches);
2069 $refID=$matches[0][0];
2070
2071 $str = str_replace("[WEBFOLDER_ID]", $refID, $str);
2072 $str = str_replace("[WEBFOLDER_TITLE]", $webfolderTitle, $str);
2073 $str = str_replace("[WEBFOLDER_URI]", $webfolderURI, $str);
2074 $str = str_replace("[WEBFOLDER_URI_IE]", $webfolderURI_IE, $str);
2075 $str = str_replace("[WEBFOLDER_URI_KONQUEROR]", $webfolderURI_Konqueror, $str);
2076 $str = str_replace("[WEBFOLDER_URI_NAUTILUS]", $webfolderURI_Nautilus, $str);
2077 $str = str_replace("[ADMIN_MAIL]", $ilSetting->get("admin_email"), $str);
2078
2079 if(strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')!==false){
2080 $str = preg_replace('/\[IF_IEXPLORE\]((?:.|\n)*)\[\/IF_IEXPLORE\]/','\1', $str);
2081 }else{
2082 $str = preg_replace('/\[IF_NOTIEXPLORE\]((?:.|\n)*)\[\/IF_NOTIEXPLORE\]/','\1', $str);
2083 }
2084
2085 switch ($os)
2086 {
2087 case 'windows' :
2088 $operatingSystem = 'WINDOWS';
2089 break;
2090 case 'unix' :
2091 switch ($osFlavor)
2092 {
2093 case 'osx' :
2094 $operatingSystem = 'MAC';
2095 break;
2096 case 'linux' :
2097 $operatingSystem = 'LINUX';
2098 break;
2099 default :
2100 $operatingSystem = 'LINUX';
2101 break;
2102 }
2103 break;
2104 default :
2105 $operatingSystem = 'UNKNOWN';
2106 break;
2107 }
2108
2109 if ($operatingSystem != 'UNKNOWN')
2110 {
2111 $str = preg_replace('/\[IF_'.$operatingSystem.'\]((?:.|\n)*)\[\/IF_'.$operatingSystem.'\]/','\1', $str);
2112 $str = preg_replace('/\[IF_([A-Z_]+)\](?:(?:.|\n)*)\[\/IF_\1\]/','', $str);
2113 }
2114 else
2115 {
2116 $str = preg_replace('/\[IF_([A-Z_]+)\]((?:.|\n)*)\[\/IF_\1\]/','\2', $str);
2117 }
2118 return $str;
2119 }
2120
2126 private function getUploadMaxFilesize() {
2127 $val = ini_get('upload_max_filesize');
2128
2129 $val = trim($val);
2130 $last = strtolower($val[strlen($val)-1]);
2131 switch($last) {
2132 // The 'G' modifier is available since PHP 5.1.0
2133 case 'g':
2134 $val *= 1024;
2135 case 'm':
2136 $val *= 1024;
2137 case 'k':
2138 $val *= 1024;
2139 }
2140
2141 return $val;
2142 }
2143}
2144// END WebDAV
2145?>
$result
$success
Definition: Utf8Test.php:87
$_GET["client_id"]
HTTP_WebDAV_Server()
Constructor.
Definition: Server.php:108
mkprop()
helper for property element creation
Definition: Server.php:1551
http_status($status)
set HTTP return status and mirror it in a private header
Definition: Server.php:1924
toNFC( $string)
Convert a UTF-8 string to normal form C, canonical composition.
Definition: UtfNormal.php:157
_recordWriteEvent($obj_id, $usr_id, $action, $parent_obj_id=null)
Records a write event.
_catchupWriteEvents($obj_id, $usr_id, $timestamp=null)
Catches up with all write events which occured before the specified timestamp.
_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.
getDir(&$objDAV, &$options)
GET method handler for directories.
$clientBrowser
The name of some well known browsers, that need special support.
LOCK(&$options)
LOCK method handler.
getMountURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null, $genericURI=false)
Returns an URI for mounting the repository object as a webfolder.
isWebDAVoverHTTPS()
Returns true, if the WebDAV server transfers data over HTTPS.
getFileURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a file object using WebDAV.
$isDebug
The WebDAVServer prints lots of log messages to the ilias log, if this variable is set to true.
davUrlEncode($path)
We do not implement this method, because authentication is done by ilias3/webdav.php.
GET(&$options)
GET method handler.
MKCOL($options)
MKCOL method handler.
davBasename($path)
Private implementation of PHP basename() function.
writelog($message)
Writes a message to the logfile.,.
static _isActionsVisible()
Static getter.
DELETE($options)
DELETE method handler.
getLogin($userId)
Returns the login for the specified user id, or null if the user does not exist.
static getInstance()
Get singelton iunstance.
PUTfinished(&$options)
PUTfinished handler.
ilDAVServer()
Constructor.
$cachedObjectDAV
Cached object handler.
davDeslashify($path)
davDeslashify - make sure path does not end in a slash
fileinfo($resourcePath, $displayPath, &$objDAV)
Creates file info properties for a single file/resource.
checkLock($path)
checkLock() helper
PUT(&$options)
PUT method handler.
$clientOS
The operating system of the WebDAV client.
showMountInstructions(&$objDAV, &$options)
Mount instructions method handler for directories.
static _isActive()
Static getter.
toObjectPath($davPath)
Converts a DAV path into an array of DAV objects.
$locks
Handler for locks.
getObjectURI($refId, $ressourceName=null, $parentRefId=null)
Returns an URI for getting a object using WebDAV by its name.
getUploadMaxFilesize()
Gets the maximum permitted upload filesize from php.ini in bytes.
PROPFIND(&$options, &$files)
PROPFIND method handler.
UNLOCK(&$options)
UNLOCK method handler.
static _getDefaultWebfolderInstructions()
Gets instructions for the usage of webfolders.
isFileHidden(&$objDAV)
Returns true, if the resource has a file name which is hidden from the user.
getObject($davPath)
Gets a DAV object for the specified path.
getFolderURI($refId, $nodeId=0, $ressourceName=null, $parentRefId=null)
Returns an URI for mounting the repository object as a webfolder using Internet Explorer and Firefox ...
static _getWebfolderInstructionsFor($webfolderTitle, $webfolderURI, $webfolderURI_IE, $webfolderURI_Konqueror, $webfolderURI_Nautilus, $os='unknown', $osFlavor='unknown')
Gets Webfolder mount instructions for the specified webfolder.
mountDir(&$objDAV, &$options)
Mount method handler for directories.
PROPPATCH(&$options)
PROPPATCH method handler.
MOVE($options)
MOVE method handler.
toNodePath($davPath)
serveRequest()
Serves a WebDAV request.
COPY($options, $del=false)
COPY method handler.
$clientOSFlavor
The flavor of the operating system of the WebDAV client.
$properties
Handler for properties.
static getValidFilename($a_filename)
Get valid filename.
HTTPS.
static lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=false)
static _lookupLogin($a_user_id)
lookup login
createObject($refId, $type)
Static factory method to create a DAV object for a given refId and type.
ILIAS Setting Class.
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
$data
$info
Definition: example_052.php:80
$GLOBALS['PHPCAS_CLIENT']
This global variable is used by the interface class phpCAS.
Definition: CAS.php:276
exit
Definition: login.php:54
Virtual base class for implementing WebDAV servers.
global $lng
Definition: privfeed.php:40
global $ilSetting
Definition: privfeed.php:40
$ref_id
Definition: sahs_server.php:39
if(!is_array($argv)) $options
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
global $ilUser
Definition: imgupload.php:15
global $https
Definition: imgupload.php:15