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