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