Implement #015359: access.php - new MatchOrder=host_uri
[tinyz:tinyz.git] / kernel / classes / webdav / ezwebdavcontentserver.php
1 <?php
2 //
3 // This is the eZWebDAVContentServer class. Manages WebDAV sessions.
4 //
5 // Created on: <15-Aug-2003 15:15:15 bh>
6 //
7 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
8 // SOFTWARE NAME: eZ Publish
9 // SOFTWARE RELEASE: 4.1.x
10 // COPYRIGHT NOTICE: Copyright (C) 1999-2010 eZ Systems AS
11 // SOFTWARE LICENSE: GNU General Public License v2.0
12 // NOTICE: >
13 //   This program is free software; you can redistribute it and/or
14 //   modify it under the terms of version 2.0  of the GNU General
15 //   Public License as published by the Free Software Foundation.
16 //
17 //   This program is distributed in the hope that it will be useful,
18 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 //   GNU General Public License for more details.
21 //
22 //   You should have received a copy of version 2.0 of the GNU General
23 //   Public License along with this program; if not, write to the Free
24 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 //   MA 02110-1301, USA.
26 //
27 //
28 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
29 //
30
31 /*!
32   \class eZWebDAVContentServer ezwebdavcontentserver.php
33   \ingroup eZWebDAV
34   \brief Provides access to eZ Publish kernel using WebDAV
35
36 */
37
38 class eZWebDAVContentServer extends eZWebDAVServer
39 {
40     const WEBDAV_INI_FILE = "webdav.ini";
41     const WEBDAV_AUTH_REALM = "eZ Publish WebDAV interface";
42     const WEBDAV_AUTH_FAILED = "Invalid username or password!";
43     const WEBDAV_INVALID_SITE = "Invalid site name specified!";
44     const WEBDAV_DISABLED = "WebDAV functionality is disabled!";
45
46     /*!
47      Initializes the eZWebDAVServer
48     */
49     function eZWebDAVContentServer()
50     {
51         $this->eZWebDAVServer();
52         $this->User = eZUser::currentUser();
53         $this->FolderClasses = null;
54     }
55
56     /*!
57      Makes sure $this->User is reinitialized with the current user,
58      then calls the $super->processClientRequest().
59     */
60     function processClientRequest()
61     {
62         $this->User = eZUser::currentUser();
63         eZWebDAVServer::processClientRequest();
64     }
65
66     /*!
67       @{
68     */
69
70     /*!
71       Fetch the file from eZCluster if needed before send.
72     */
73     function outputSendDataToClient( $output, $headers_only = false )
74     {
75         if ( $output["file"] )
76         {
77             $realPath = $output["file"];
78             $file = eZClusterFileHandler::instance( $realPath );
79             $file->fetch();
80         }
81         $result = eZWebDAVServer::outputSendDataToClient($output,$headers_only);
82         if ( $output["file"] && is_object( $file ) )
83             $file->deleteLocal();
84         return $result;
85     }
86
87     /*!
88       Restricts the allowed methods to only the subset that this server supports.
89     */
90     function options( $target )
91     {
92         // Only a few WebDAV operations are allowed for now.
93         $options = array();
94         $options['methods'] = array( 'OPTIONS', 'PROPFIND', 'HEAD', 'GET', 'PUT', 'MKCOL', 'MOVE' );
95 //         $options['versions'] = array( '1' );
96
97         return $options;
98     }
99
100     /*!
101       Produces the collection content. Builds either the virtual start folder
102       with the virtual content folder in it (and additional files). OR: if
103       we're browsing within the content folder: it gets the content of the
104       target/given folder.
105     */
106     function getCollectionContent( $collection, $depth = false, $properties = false )
107     {
108         $fullPath = $collection;
109         $collection = $this->splitFirstPathElement( $collection, $currentSite );
110
111         if ( !$currentSite )
112         {
113             // Display the root which contains a list of sites
114             $this->appendLogEntry( "Root: Fethcing site list", 'CS:getCollectionContent' );
115             $entries = $this->fetchSiteListContent( $depth, $properties );
116             return $entries;
117         }
118
119         if ( !$this->userHasSiteAccess( $currentSite ) )
120         {
121             $this->appendLogEntry( "No access to site '$currentSite'", 'CS:getCollectionContent' );
122             return eZWebDAVServer::FAILED_FORBIDDEN;
123         }
124
125         return $this->getVirtualFolderCollection( $currentSite, $collection, $fullPath, $depth, $properties );
126     }
127
128     /*!
129      \private
130      Handles collections on the virtual folder level, if no virtual folder
131      elements are accessed it lists the virtual folders.
132     */
133     function getVirtualFolderCollection( $currentSite, $collection, $fullPath, $depth, $properties )
134     {
135         $this->appendLogEntry( "Check virtual folder: site '$currentSite' in '$collection' ", 'CS:getVirtualFolderCollection' );
136         $this->setCurrentSite( $currentSite );
137
138         if ( !$collection )
139         {
140             // We are inside a site so we display the virtual folder for the site
141             $this->appendLogEntry( "Virtual folder for '$currentSite'", 'CS:getVirtualFolderCollection' );
142             $entries = $this->fetchVirtualSiteContent( $currentSite, $depth, $properties );
143             return $entries;
144         }
145
146         $collection = $this->splitFirstPathElement( $collection, $virtualFolder );
147
148         if ( !in_array( $virtualFolder, $this->virtualFolderList() ) )
149         {
150             $this->appendLogEntry( "Unknown virtual folder: '$virtualFolder' in site '$currentSite'", 'CS:getVirtualFolderCollection' );
151             return eZWebDAVServer::FAILED_NOT_FOUND;
152         }
153
154         if ( !$this->userHasVirtualAccess( $currentSite, $virtualFolder ) )
155         {
156             $this->appendLogEntry( "No access to virtual folder '$virtualFolder' in site '$currentSite'", 'CS:getVirtualFolderCollection' );
157             return eZWebDAVServer::FAILED_FORBIDDEN;
158         }
159
160         return $this->getContentTreeCollection( $currentSite, $virtualFolder, $collection, $fullPath, $depth, $properties );
161     }
162
163     /*!
164      \private
165      Handles collections on the content tree level.
166      Depending on the virtual folder we will generate a node path url and fetch
167      the nodes for that path.
168     */
169     function getContentTreeCollection( $currentSite, $virtualFolder, $collection, $fullPath, $depth, $properties )
170     {
171         $this->appendLogEntry( "Content collection: from site '$currentSite' in '$virtualFolder' using path '$collection'", 'CS:getContentTreeCollection' );
172         $nodePath = $this->internalNodePath( $virtualFolder, $collection );
173         $node = $this->fetchNodeByTranslation( $nodePath );
174
175         if ( !$node )
176         {
177             $this->appendLogEntry( "Unknown node: $nodePath", 'CS:getContentTreeCollection' );
178             return eZWebDAVServer::FAILED_NOT_FOUND;
179         }
180
181         // Can we list the children of the node?
182         if ( !$node->canRead() )
183         {
184             $this->appendLogEntry( "No access to content '$nodePath' in site '$currentSite'", 'CS:getContentTreeCollection' );
185             return eZWebDAVServer::FAILED_FORBIDDEN;
186         }
187
188         $entries = $this->fetchContentList( $node, $nodePath, $depth, $properties );
189         return $entries;
190     }
191
192     /*!
193      Tries to figure out the filepath of the object being shown,
194      if not we will pass the virtual url as the filepath.
195     */
196     function get( $target )
197     {
198         $result         = array();
199         $result["data"] = false;
200         $result["file"] = false;
201
202         $fullPath = $target;
203         $target = $this->splitFirstPathElement( $target, $currentSite );
204
205         if ( !$currentSite )
206         {
207             // Sites are folders and have no data
208             return eZWebDAVServer::FAILED_FORBIDDEN;
209         }
210
211         if ( !$this->userHasSiteAccess( $currentSite ) )
212         {
213             $this->appendLogEntry( "No access to site '$currentSite'", 'CS:get' );
214             return eZWebDAVServer::FAILED_FORBIDDEN;
215         }
216
217         return $this->getVirtualFolderData( $result, $currentSite, $target, $fullPath );
218     }
219
220     /*!
221      \private
222      Handles data retrival on the virtual folder level.
223     */
224     function getVirtualFolderData( $result, $currentSite, $target, $fullPath )
225     {
226         $this->appendLogEntry( "current site: $currentSite", 'CS:get' );
227         $this->setCurrentSite( $currentSite );
228
229         $target = $this->splitFirstPathElement( $target, $virtualFolder );
230
231         if ( !$target )
232         {
233             if ( !in_array( $virtualFolder, $this->virtualFileList() ) )
234             {
235                 return eZWebDAVServer::FAILED_NOT_FOUND;
236             }
237
238             // We have reached the end of the path
239             if ( $virtualFolder == basename( eZWebDAVContentServer::virtualInfoFileName() ) )
240             {
241                 $result["file"] = eZWebDAVContentServer::virtualInfoFileName();
242
243                 return $result;
244             }
245
246             // The rest in the virtual folder does not have any data
247             return eZWebDAVServer::FAILED_NOT_FOUND;
248         }
249
250         if ( !$this->userHasVirtualAccess( $currentSite, $virtualFolder ) )
251         {
252             $this->appendLogEntry( "No access to virtual folder '$virtualFolder' in site '$currentSite'", 'CS:get' );
253             return eZWebDAVServer::FAILED_FORBIDDEN;
254         }
255
256         if ( !in_array( $virtualFolder, $this->virtualFolderList() ) )
257         {
258             $this->appendLogEntry( "Unknown virtual folder: '$virtualFolder' in site '$currentSite'", 'CS:get' );
259             return eZWebDAVServer::FAILED_NOT_FOUND;
260         }
261
262         if ( $virtualFolder == eZWebDAVContentServer::virtualContentFolderName() or
263              $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
264         {
265             return $this->getContentNodeData( $result, $currentSite, $virtualFolder, $target, $fullPath );
266         }
267         return eZWebDAVServer::FAILED_NOT_FOUND;
268     }
269
270     /*!
271      \private
272      Handles data retrival on the content tree level.
273     */
274     function getContentNodeData( $result, $currentSite, $virtualFolder, $target, $fullPath )
275     {
276         $this->appendLogEntry( "attempting to fetch node, target is: $target", 'CS:get' );
277
278         // Attempt to fetch the node the client wants to get.
279         $nodePath = $this->internalNodePath( $virtualFolder, $target );
280         $node = $this->fetchNodeByTranslation( $nodePath );
281
282         // Proceed only if the node is valid:
283         if ( $node == null )
284         {
285             $this->appendLogEntry( "No node for: $nodePath", 'CS:get' );
286             return $result;
287         }
288
289         // Can we fetch the contents of the node
290         if ( !$node->canRead() )
291         {
292             $this->appendLogEntry( "No access to get '$nodePath' in site '$currentSite'", 'CS:get' );
293             return eZWebDAVServer::FAILED_FORBIDDEN;
294         }
295
296         $object = $node->attribute( 'object' );
297
298         $upload = new eZContentUpload();
299         $info = $upload->objectFileInfo( $object );
300         if ( $info )
301         {
302             $result['file'] = $info['filepath'];
303         }
304
305         return $result;
306     }
307
308     /*!
309      \note Not implemented yet
310     */
311     function head( $target )
312     {
313         return eZWebDAVServer::FAILED_NOT_FOUND;
314     }
315
316     /*!
317      Tries to create/update an object at location \a $target with the file \a $tempFile.
318     */
319     function put( $target, $tempFile )
320     {
321         $fullPath = $target;
322         $target = $this->splitFirstPathElement( $target, $currentSite );
323
324         if ( !$currentSite )
325         {
326             return eZWebDAVServer::FAILED_FORBIDDEN;
327         }
328
329         if ( !$this->userHasSiteAccess( $currentSite ) )
330         {
331             $this->appendLogEntry( "No access to site '$currentSite'", 'CS:put' );
332             return eZWebDAVServer::FAILED_FORBIDDEN;
333         }
334
335         return $this->putVirtualFolderData( $currentSite, $target, $tempFile, $fullPath );
336     }
337
338     /*!
339      \private
340      Handles data storage on the content tree level.
341      It will check if the target is below a content folder in which it calls putContentData().
342     */
343     function putVirtualFolderData( $currentSite, $target, $tempFile, $fullPath )
344     {
345         $this->appendLogEntry( "current site is: $currentSite", 'CS:put' );
346         $this->setCurrentSite( $currentSite );
347
348         $target = $this->splitFirstPathElement( $target, $virtualFolder );
349
350         if ( !$target )
351         {
352             // We have reached the end of the path
353             // We do not allow 'put' operations for the virtual folder.
354             return eZWebDAVServer::FAILED_FORBIDDEN;
355         }
356
357         if ( !in_array( $virtualFolder, $this->virtualFolderList() ) )
358         {
359             $this->appendLogEntry( "Unknown virtual folder: '$virtualFolder' in site '$currentSite'", 'CS:put' );
360             return eZWebDAVServer::FAILED_CONFLICT;
361         }
362
363         if ( !$this->userHasVirtualAccess( $currentSite, $virtualFolder ) )
364         {
365             $this->appendLogEntry( "No access to virtual folder '$virtualFolder' in site '$currentSite'", 'CS:put' );
366             return eZWebDAVServer::FAILED_FORBIDDEN;
367         }
368
369         if ( $virtualFolder == eZWebDAVContentServer::virtualContentFolderName() or
370              $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
371         {
372             return $this->putContentData( $currentSite, $virtualFolder, $target, $tempFile, $fullPath );
373         }
374
375         return eZWebDAVServer::FAILED_FORBIDDEN;
376     }
377
378     /*!
379      \private
380      Handles data storage on the content tree level.
381      It will try to find the parent node of the wanted placement and
382      create a new object with data from \a $tempFile.
383     */
384     function putContentData( $currentSite, $virtualFolder, $target, $tempFile, $fullPath )
385     {
386         $nodePath = $this->internalNodePath( $virtualFolder, $target );
387
388         $this->appendLogEntry( "Inside virtual content folder", 'CS:put' );
389
390         $parentNode = $this->fetchParentNodeByTranslation( $nodePath );
391         if ( $parentNode == null )
392         {
393             // The node does not exist, so we cannot put the file
394             $this->appendLogEntry( "Cannot put file $nodePath, not parent found", 'CS:put' );
395             return eZWebDAVServer::FAILED_CONFLICT;
396         }
397
398         // Can we put content in the parent node
399         if ( !$parentNode->canRead() )
400         {
401             $this->appendLogEntry( "No access to put '$nodePath' in site '$currentSite'", 'CS:put' );
402             return eZWebDAVServer::FAILED_FORBIDDEN;
403         }
404
405         $parentNodeID = $parentNode->attribute( 'node_id' );
406
407         // We need the MIME-Type to figure out which content-class we will use
408         $mimeInfo = eZMimeType::findByURL( $nodePath );
409         $mime = $mimeInfo['name'];
410
411         $webdavINI = eZINI::instance( eZWebDAVContentServer::WEBDAV_INI_FILE );
412         $defaultObjectType = $webdavINI->variable( 'PutSettings', 'DefaultClass' );
413
414         $existingNode = $this->fetchNodeByTranslation( $nodePath );
415         $upload = new eZContentUpload();
416         if ( !$upload->handleLocalFile( $result, $tempFile, $parentNodeID, $existingNode ) )
417         {
418             foreach ( $result['errors'] as $error )
419             {
420                 $this->appendLogEntry( "Error: " . $error['description'], 'CS: put' );
421             }
422             foreach ( $result['notices'] as $notice )
423             {
424                 $this->appendLogEntry( "Notice: " . $notice['description'], 'CS: put' );
425             }
426             if ( $result['status'] == eZContentUpload::STATUS_PERMISSION_DENIED )
427             {
428                 return eZWebDAVServer::FAILED_FORBIDDEN;
429             }
430             else
431                 return eZWebDAVServer::FAILED_UNSUPPORTED;
432         }
433
434         return eZWebDAVServer::OK_CREATED;
435     }
436
437     /*!
438       Tries to create a collection at \a $target. In our case this is a content-class
439       of a given type (most likely a folder).
440     */
441     function mkcol( $target )
442     {
443         $fullPath = $target;
444         $target = $this->splitFirstPathElement( $target, $currentSite );
445
446         if ( !$currentSite )
447         {
448             // Site list cannot get new entries
449             return eZWebDAVServer::FAILED_FORBIDDEN;
450         }
451
452         if ( !$this->userHasSiteAccess( $currentSite ) )
453         {
454             $this->appendLogEntry( "No access to site '$currentSite'", 'CS:mkcol' );
455             return eZWebDAVServer::FAILED_FORBIDDEN;
456         }
457
458         return $this->mkcolVirtualFolder( $currentSite, $target, $fullPath );
459     }
460
461     /*!
462      \private
463      Handles collection creation on the virtual folder level.
464      It will check if the target is below a content folder in which it calls mkcolContent().
465     */
466     function mkcolVirtualFolder( $currentSite, $target, $fullPath )
467     {
468         $this->setCurrentSite( $currentSite );
469
470         $target = $this->splitFirstPathElement( $target, $virtualFolder );
471
472         if ( !in_array( $virtualFolder, $this->virtualList() ) )
473         {
474             $this->appendLogEntry( "Unknown virtual element: '$virtualFolder' in site '$currentSite'", 'CS:mkcol' );
475             return eZWebDAVServer::FAILED_NOT_FOUND;
476         }
477
478         if ( !$target )
479         {
480             // We have reached the end of the path
481             // We do not allow 'mkcol' operations for the virtual folder.
482             return eZWebDAVServer::FAILED_FORBIDDEN;
483         }
484
485         if ( !$this->userHasVirtualAccess( $currentSite, $virtualFolder ) )
486         {
487             $this->appendLogEntry( "No access to virtual folder '$virtualFolder' in site '$currentSite'", 'CS:mkcol' );
488             return eZWebDAVServer::FAILED_FORBIDDEN;
489         }
490
491         if ( $virtualFolder == eZWebDAVContentServer::virtualContentFolderName() or
492              $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
493         {
494             return $this->mkcolContent( $currentSite, $virtualFolder, $target, $fullPath );
495         }
496
497         return eZWebDAVServer::FAILED_FORBIDDEN;
498     }
499
500     /*!
501      \private
502      Handles collection creation on the content tree level.
503      It will try to find the parent node of the wanted placement and
504      create a new collection (folder etc.) as a child.
505     */
506     function mkcolContent( $currentSite, $virtualFolder, $target, $fullPath )
507     {
508         $nodePath = $this->internalNodePath( $virtualFolder, $target );
509         $node = $this->fetchNodeByTranslation( $nodePath );
510         if ( $node )
511         {
512             return eZWebDAVServer::FAILED_EXISTS;
513         }
514
515         $parentNode = $this->fetchParentNodeByTranslation( $nodePath );
516         $this->appendLogEntry( "Target is: $target", 'CS:mkcolContent' );
517
518         if ( !$parentNode )
519         {
520             return eZWebDAVServer::FAILED_NOT_FOUND;
521         }
522
523         // Can we create a collection in the parent node
524         if ( !$parentNode->canRead() )
525         {
526             $this->appendLogEntry( "No access to mkcol '$nodePath' in site '$currentSite'", 'CS:mkcolContent' );
527             return eZWebDAVServer::FAILED_FORBIDDEN;
528         }
529
530         return $this->createFolder( $parentNode, $nodePath );
531     }
532
533     /*!
534       Removes the object from the node tree and leaves it in the trash.
535     */
536     function delete( $target )
537     {
538         $fullPath = $target;
539         $target = $this->splitFirstPathElement( $target, $currentSite );
540
541         if ( !$currentSite )
542         {
543             // Cannot delete entries in site list
544             return eZWebDAVServer::FAILED_FORBIDDEN;
545         }
546
547         if ( !$this->userHasSiteAccess( $currentSite ) )
548         {
549             $this->appendLogEntry( "No access to site '$currentSite'", 'CS:delete' );
550             return eZWebDAVServer::FAILED_FORBIDDEN;
551         }
552
553         return $this->deleteVirtualFolder( $currentSite, $target, $fullPath );
554     }
555
556     /*!
557      \private
558      Handles deletion on the virtual folder level.
559      It will check if the target is below a content folder in which it calls deleteContent().
560     */
561     function deleteVirtualFolder( $currentSite, $target, $fullPath )
562     {
563         $this->appendLogEntry( "Target is: $target", 'CS:delete' );
564         $this->setCurrentSite( $currentSite );
565
566         $target = $this->splitFirstPathElement( $target, $virtualFolder );
567
568         if ( !in_array( $virtualFolder, $this->virtualList() ) )
569         {
570             $this->appendLogEntry( "Unknown virtual element: '$virtualFolder' in site '$currentSite'", 'CS:deleteVirtualFolder' );
571             return eZWebDAVServer::FAILED_NOT_FOUND;
572         }
573
574         if ( !$target )
575         {
576             // We have reached the end of the path
577             // We do not allow 'delete' operations for the virtual folder.
578             return eZWebDAVServer::FAILED_FORBIDDEN;
579         }
580
581         if ( !$this->userHasVirtualAccess( $currentSite, $virtualFolder ) )
582         {
583             $this->appendLogEntry( "No access to virtual folder '$virtualFolder' in site '$currentSite'", 'CS:deleteVirtualFolder' );
584             return eZWebDAVServer::FAILED_FORBIDDEN;
585         }
586
587         if ( $virtualFolder == eZWebDAVContentServer::virtualContentFolderName() or
588              $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
589         {
590             return $this->deleteContent( $currentSite, $virtualFolder, $target, $fullPath );
591         }
592
593         return eZWebDAVServer::FAILED_FORBIDDEN;
594     }
595
596     /*!
597      \private
598      Handles deletion on the content tree level.
599      It will try to find the node of the target \a $target
600      and then try to remove it (ie. move to trash) if the user is allowed.
601     */
602     function deleteContent( $currentSite, $virtualFolder, $target, $fullPath )
603     {
604         $nodePath = $this->internalNodePath( $virtualFolder, $target );
605         $node = $this->fetchNodeByTranslation( $nodePath );
606
607         if ( $node == null )
608         {
609             $this->appendLogEntry( "Cannot delete node/object $nodePath, it does not exist", 'CS:deleteContent' );
610             return eZWebDAVServer::FAILED_NOT_FOUND;
611         }
612
613         // Can we delete the node?
614         if ( !$node->canRead() or
615              !$node->canRemove() )
616         {
617             $this->appendLogEntry( "No access to delete '$nodePath' in site '$currentSite'", 'CS:deleteContent' );
618             return eZWebDAVServer::FAILED_FORBIDDEN;
619         }
620
621         $this->appendLogEntry( "Removing node: $nodePath", 'CS:deleteContent' );
622         $node->removeNodeFromTree( true );
623         return eZWebDAVServer::OK;
624     }
625
626     /*!
627       Moves the object \a $source to destination \a $destination.
628     */
629     function move( $source, $destination )
630     {
631         $fullSource = $source;
632         $fullDestination = $destination;
633         $source = $this->splitFirstPathElement( $source, $sourceSite );
634         $destination = $this->splitFirstPathElement( $destination, $destinationSite );
635         if ( $sourceSite != $destinationSite )
636         {
637             // We do not support moving from one site to another yet
638             // TODO: Check if the sites are using the same db,
639             //       if so allow the move as a simple object move
640             //       If not we will have to do an object export from
641             //       $sourceSite and import it in $destinationSite
642             return eZWebDAVServer::FAILED_FORBIDDEN;
643         }
644
645         if ( !$sourceSite or
646              !$destinationSite )
647         {
648             // Cannot move entries in site list
649             return eZWebDAVServer::FAILED_FORBIDDEN;
650         }
651
652         if ( !$this->userHasSiteAccess( $sourceSite ) )
653         {
654             $this->appendLogEntry( "No access to site '$sourceSite'", 'CS:move' );
655             return eZWebDAVServer::FAILED_FORBIDDEN;
656         }
657         if ( !$this->userHasSiteAccess( $destinationSite ) )
658         {
659             $this->appendLogEntry( "No access to site '$destinationSite'", 'CS:move' );
660             return eZWebDAVServer::FAILED_FORBIDDEN;
661         }
662
663         return $this->moveVirtualFolder( $sourceSite, $destinationSite,
664                                          $source, $destination,
665                                          $fullSource, $fullDestination );
666     }
667
668     /*!
669      \private
670      Handles moving on the virtual folder level.
671      It will check if the target is below a content folder in which it calls moveContent().
672     */
673     function moveVirtualFolder( $sourceSite, $destinationSite,
674                                 $source, $destination,
675                                 $fullSource, $fullDestination )
676     {
677         $this->setCurrentSite( $sourceSite );
678
679         $source = $this->splitFirstPathElement( $source, $sourceVFolder );
680         $destination = $this->splitFirstPathElement( $destination, $destinationVFolder );
681
682         if ( !in_array( $sourceVFolder, $this->virtualList() ) )
683         {
684             $this->appendLogEntry( "Unknown virtual element: '$sourceVFolder' in site '$sourceSite'", 'CS:moveVirtualFolder' );
685             return eZWebDAVServer::FAILED_NOT_FOUND;
686         }
687
688         if ( !in_array( $destinationVFolder, $this->virtualList() ) )
689         {
690             $this->appendLogEntry( "Unknown virtual element: '$destinationVFolder' in site '$destinationSite'", 'CS:moveVirtualFolder' );
691             return eZWebDAVServer::FAILED_NOT_FOUND;
692         }
693
694         if ( !$source or
695              !$destination )
696         {
697             // We have reached the end of the path for source or destination
698             // We do not allow 'move' operations for the virtual folder (from or to)
699             return eZWebDAVServer::FAILED_FORBIDDEN;
700         }
701
702         if ( !$this->userHasVirtualAccess( $sourceSite, $sourceVFolder ) )
703         {
704             $this->appendLogEntry( "No access to virtual folder '$sourceVFolder' in site '$sourceSite'", 'CS:moveVirtualFolder' );
705             return eZWebDAVServer::FAILED_FORBIDDEN;
706         }
707         if ( !$this->userHasVirtualAccess( $destinationSite, $destinationVFolder ) )
708         {
709             $this->appendLogEntry( "No access to virtual folder '$destinationVFolder' in site '$destinationSite'", 'CS:moveVirtualFolder' );
710             return eZWebDAVServer::FAILED_FORBIDDEN;
711         }
712
713         if ( ( $sourceVFolder == eZWebDAVContentServer::virtualContentFolderName() or
714                $sourceVFolder == eZWebDAVContentServer::virtualMediaFolderName() ) and
715              ( $destinationVFolder == eZWebDAVContentServer::virtualContentFolderName() or
716                $destinationVFolder == eZWebDAVContentServer::virtualMediaFolderName() ) )
717         {
718             return $this->moveContent( $sourceSite, $destinationSite,
719                                        $sourceVFolder, $destinationVFolder,
720                                        $source, $destination,
721                                        $fullSource, $fullDestination );
722         }
723
724         return eZWebDAVServer::FAILED_FORBIDDEN;
725     }
726
727     /*!
728      \private
729      Handles moving on the content tree level.
730      It will try to find the node of the target \a $source
731      and then try to move it to \a $destination.
732     */
733     function moveContent( $sourceSite, $destinationSite,
734                           $sourceVFolder, $destinationVFolder,
735                           $source, $destination,
736                           $fullSource, $fullDestination )
737     {
738         $nodePath = $this->internalNodePath( $sourceVFolder, $source );
739         $destinationNodePath = $this->internalNodePath( $destinationVFolder, $destination );
740
741         // Get rid of possible extensions, remove .jpeg .txt .html etc..
742         $source = $this->fileBasename( $source );
743
744         $sourceNode = $this->fetchNodeByTranslation( $nodePath );
745
746         if ( !$sourceNode )
747         {
748             return eZWebDAVServer::FAILED_NOT_FOUND;
749         }
750
751         // Can we move the node from $sourceNode
752         if ( !$sourceNode->canMoveFrom() )
753         {
754             $this->appendLogEntry( "No access to move the node '$sourceSite':'$nodePath'", 'CS:moveContent' );
755             return eZWebDAVServer::FAILED_FORBIDDEN;
756         }
757
758         $object = $sourceNode->attribute( 'object' );
759         $classID = $object->attribute( 'contentclass_id' );
760
761         // Get rid of possible extensions, remove .jpeg .txt .html etc..
762         $destination = $this->fileBasename( $destination );
763
764         $destinationNode = $this->fetchNodeByTranslation( $destinationNodePath );
765         $this->appendLogEntry( "Destination: $destinationNodePath", 'CS:moveContent' );
766
767         if ( $destinationNode )
768         {
769             return eZWebDAVServer::FAILED_EXISTS;
770         }
771
772         $destinationNode = $this->fetchParentNodeByTranslation( $destinationNodePath );
773
774         if ( !$destinationNode )
775         {
776             return eZWebDAVServer::FAILED_NOT_FOUND;
777         }
778
779         // Can we move the node to $destinationNode
780         if ( !$destinationNode->canMoveTo( $classID ) )
781         {
782             $this->appendLogEntry( "No access to move the node '$sourceSite':'$nodePath' to '$destinationSite':'$destinationNodePath'", 'CS:moveContent' );
783             return eZWebDAVServer::FAILED_FORBIDDEN;
784         }
785
786         $srcParentPath = $this->splitLastPathElement( $nodePath, $srcNodeName );
787         $dstParentPath = $this->splitLastPathElement( $destinationNodePath, $dstNodeName );
788         if ( $srcParentPath == $dstParentPath )
789         {
790             if( !$object->rename( $dstNodeName ) )
791             {
792                 $this->appendLogEntry( "Unable to rename the node '$sourceSite':'$nodePath' to '$destinationSite':'$destinationNodePath'", 'CS:moveContent' );
793                 return eZWebDAVServer::FAILED_FORBIDDEN;
794             }
795         }
796         else
797         {
798             if( !eZContentObjectTreeNodeOperations::move( $sourceNode->attribute( 'node_id' ), $destinationNode->attribute( 'node_id' ) ) )
799             {
800                 $this->appendLogEntry( "Unable to move the node '$sourceSite':'$nodePath' to '$destinationSite':'$destinationNodePath'", 'CS:moveContent' );
801                 return eZWebDAVServer::FAILED_FORBIDDEN;
802             }
803         }
804
805         /*
806
807         // Todo: add lookup of the name setting for the current object
808                     $contentObjectID = $object->attribute( 'id' );
809                     $contentObjectAttributes =& $object->contentObjectAttributes();
810                     $contentObjectAttributes[0]->setAttribute( 'data_text', basename( $destination ) );
811                     $contentObjectAttributes[0]->store();
812
813                     //include_once( 'lib/ezutils/classes/ezoperationhandler.php' );
814                     $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID, 'version' => 1 ) );
815                     $object->store();
816         */
817
818         return eZWebDAVServer::OK_CREATED;
819     }
820
821     /*!
822      @}
823     */
824
825     /*!
826       Sets/changes the current site(access) to a \a $site.
827     */
828     function setCurrentSite( $site )
829     {
830         $access = array( 'name' => $site,
831                          'type' => eZSiteAccess::TYPE_STATIC );
832
833         $access = eZSiteAccess::change( $access );
834         eZDebugSetting::writeDebug( 'kernel-siteaccess', $access, 'current siteaccess' );
835
836         // Clear/flush global database instance.
837         $nullVar = null;
838         eZDB::setInstance( $nullVar );
839     }
840
841     /*!
842       Checks if the current user has access rights to site \a $site.
843       \return \c true if the user proper access.
844     */
845     function userHasSiteAccess( $site )
846     {
847         $result = $this->User->hasAccessTo( 'user', 'login' );
848         $accessWord = $result['accessWord'];
849
850         if ( $accessWord == 'limited' )
851         {
852             $hasAccess = false;
853             $policyChecked = false;
854             foreach ( array_keys( $result['policies'] ) as $key )
855             {
856                 $policy =& $result['policies'][$key];
857                 if ( isset( $policy['SiteAccess'] ) )
858                 {
859                     $policyChecked = true;
860                     if ( in_array( eZSys::ezcrc32( $site ), $policy['SiteAccess'] ) )
861                     {
862                         $hasAccess = true;
863                         break;
864                     }
865                 }
866                 if ( $hasAccess )
867                     break;
868             }
869             if ( !$policyChecked )
870                 $hasAccess = true;
871         }
872         else if ( $accessWord == 'yes' )
873         {
874             $hasAccess = true;
875         }
876         else if ( $accessWord == 'no' )
877         {
878             $hasAccess = false;
879         }
880         return $hasAccess;
881     }
882
883     /*!
884       Checks if the current user has access rights to virtual element \a virtual
885       on site \a $site.
886       \return \c true if the user proper access.
887     */
888     function userHasVirtualAccess( $site, $virtual )
889     {
890         $this->appendLogEntry( "Can access '$site' and '$virtual'", 'CS:userHasVirtualAccess' );
891         return true;
892     }
893
894     /*!
895       Detects a possible/valid site-name in start of a path.
896       \return The name of the site that was detected or \c false if not site could be detected
897     */
898     function currentSiteFromPath( $path )
899     {
900         $this->appendLogEntry( "start path: $path", 'CS:currentSiteFromPath' );
901
902         $indexDir = eZSys::indexDir();
903
904         // Remove indexDir if used in non-virtualhost mode.
905         if ( preg_match( "#^" . preg_quote( $indexDir ) . "(.+)$#", $path, $matches ) )
906         {
907             $path = $matches[1];
908         }
909
910         $this->appendLogEntry( "indexdir: $path", 'CS:currentSiteFromPath' );
911
912         // Get the list of available sites.
913         $sites = $this->availableSites();
914
915         foreach ( $sites as $site )
916         {
917             // Check if given path starts with this site-name, if so: return it.
918             if ( preg_match( "#^/" . preg_quote( $site ) . "(.*)$#", $path, $matches ) )
919             {
920                 $this->appendLogEntry( "site $site: $path", 'CS:currentSiteFromPath' );
921                 return $site ;
922             }
923         }
924
925         $this->appendLogEntry( "no valid site was found..", 'CS:currentSiteFromPath' );
926         return false ;
927     }
928
929     /*!
930       Removes the www-dir and indexfile from the URL.
931     */
932     function processURL( $url )
933     {
934         $this->appendLogEntry( "start url: $url", 'CS:processURL' );
935         $indexDir = eZSys::indexDir();
936         $len = strlen( $indexDir );
937
938         if ( $indexDir == substr( $url, 0, $len ) )
939         {
940             $url = substr( $url, $len );
941         }
942
943         // Remove the starting / if there is one
944         // the rest of the operation code expects this to not be present
945         if ( strlen( $url ) > 0 and $url[0] == '/' )
946             $url = substr( $url, 1 );
947
948         $this->appendLogEntry( "indexdir url: $url", 'CS:processURL' );
949         return $url;
950     }
951
952     function headers()
953     {
954         header( "WebDAV-Powered-By: eZ Publish" );
955     }
956
957     /*!
958       Takes the first path element from \a $path and removes it from
959       the path, the extracted part will be placed in \a $name.
960       \return A string containing the rest of the path,
961               the path will not contain a starting slash.
962       \param $path A string defining a path of elements delimited by a slash,
963                    if the path starts with a slash it will be removed.
964       \param[out] $element The name of the first path element without any slashes.
965
966       \code
967       $path = '/path/to/item/';
968       $newPath = eZWebDAVContentServer::splitFirstPathElement( $path, $root );
969       print( $root ); // prints 'path', $newPath is now 'to/item/'
970       $newPath = eZWebDAVContentServer::splitFirstPathElement( $newPath, $second );
971       print( $second ); // prints 'to', $newPath is now 'item/'
972       $newPath = eZWebDAVContentServer::splitFirstPathElement( $newPath, $third );
973       print( $third ); // prints 'item', $newPath is now ''
974       \endcode
975     */
976     function splitFirstPathElement( $path, &$element )
977     {
978         if ( $path[0] == '/' )
979             $path = substr( $path, 1 );
980         $pos = strpos( $path, '/' );
981         if ( $pos === false )
982         {
983             $element = $path;
984             $path = '';
985         }
986         else
987         {
988             $element = substr( $path, 0, $pos );
989             $path = substr( $path, $pos + 1 );
990         }
991         return $path;
992     }
993
994     /*!
995       Takes the last path element from \a $path and removes it from
996       the path, the extracted part will be placed in \a $name.
997       \return A string containing the rest of the path,
998               the path will not contain the ending slash.
999       \param $path A string defining a path of elements delimited by a slash,
1000                    if the path ends with a slash it will be removed.
1001       \param[out] $element The name of the first path element without any slashes.
1002
1003       \code
1004       $path = '/path/to/item/';
1005       $newPath = eZWebDAVContentServer::splitLastPathElement( $path, $root );
1006       print( $root ); // prints 'item', $newPath is now '/path/to'
1007       $newPath = eZWebDAVContentServer::splitLastPathElement( $newPath, $second );
1008       print( $second ); // prints 'to', $newPath is now '/path'
1009       $newPath = eZWebDAVContentServer::splitLastPathElement( $newPath, $third );
1010       print( $third ); // prints 'path', $newPath is now ''
1011       \endcode
1012     */
1013     function splitLastPathElement( $path, &$element )
1014     {
1015         $len = strlen( $path );
1016         if ( $len > 0 and $path[$len - 1] == '/' )
1017             $path = substr( $path, 0, $len - 1 );
1018         $pos = strrpos( $path, '/' );
1019         if ( $pos === false )
1020         {
1021             $element = $path;
1022             $path = '';
1023         }
1024         else
1025         {
1026             $element = substr( $path, $pos + 1 );
1027             $path = substr( $path, 0, $pos );
1028         }
1029         return $path;
1030     }
1031
1032     /*!
1033      \private
1034      \return A path that corresponds to the internal path of nodes.
1035     */
1036     function internalNodePath( $virtualFolder, $collection )
1037     {
1038         // All root nodes needs to prepend their name to get the correct path
1039         // except for the content root which uses the path directly.
1040         if ( $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
1041         {
1042             $nodePath = 'media';
1043             if ( strlen( $collection ) > 0 )
1044                 $nodePath .= '/' . $collection;
1045         }
1046         else
1047         {
1048             $nodePath = $collection;
1049         }
1050         return $nodePath;
1051     }
1052
1053     /*!
1054       Attempts to fetch a possible/existing node by translating
1055       the inputted string/path to a node-number.
1056     */
1057     function fetchNodeByTranslation( $nodePathString )
1058     {
1059         // Get rid of possible extensions, remove .jpeg .txt .html etc..
1060         $nodePathString = $this->fileBasename( $nodePathString );
1061
1062         // Strip away last slash
1063         if ( strlen( $nodePathString ) > 0 and
1064              $nodePathString[strlen( $nodePathString ) - 1] == '/' )
1065         {
1066             $nodePathString = substr( $nodePathString, 0, strlen( $nodePathString ) - 1 );
1067         }
1068
1069         if ( strlen( $nodePathString ) > 0 )
1070         {
1071             $nodePathString = eZURLAliasML::convertPathToAlias( $nodePathString );
1072         }
1073
1074         // Attempt to get nodeID from the URL.
1075         $nodeID = eZURLAliasML::fetchNodeIDByPath( $nodePathString );
1076         if ( $nodeID )
1077         {
1078             $this->appendLogEntry( "NodeID: $nodeID", 'CS:fetchNodeByTranslation' );
1079         }
1080         else
1081         {
1082             $this->appendLogEntry( "No nodeID", 'CS:fetchNodeByTranslation' );
1083             return false;
1084         }
1085
1086         // Attempt to fetch the node.
1087         $node = eZContentObjectTreeNode::fetch( $nodeID );
1088
1089         // Return the node.
1090         return $node;
1091     }
1092
1093     /*!
1094       \return The string \a $name without the final suffix (.jpg, .gif etc.)
1095     */
1096     function fileBasename( $name )
1097     {
1098         $pos = strrpos( $name, '.' );
1099         if ( $pos !== false )
1100         {
1101             $name = substr( $name, 0, $pos );
1102         }
1103         return $name;
1104     }
1105
1106     /*!
1107       Attempts to fetch a possible node by translating
1108       the inputted string/path to a node-number. The last
1109       section of the path is removed before the actual
1110       translation: hence, the PARENT node is returned.
1111     */
1112     function fetchParentNodeByTranslation( $nodePathString )
1113     {
1114         // Strip extensions. E.g. .jpg
1115         $nodePathString = $this->fileBasename( $nodePathString );
1116
1117         // Strip away last slash
1118         if ( strlen( $nodePathString ) > 0 and
1119              $nodePathString[strlen( $nodePathString ) - 1] == '/' )
1120         {
1121             $nodePathString = substr( $nodePathString, 0, strlen( $nodePathString ) - 1 );
1122         }
1123
1124         $nodePathString = $this->splitLastPathElement( $nodePathString, $element );
1125
1126         if ( strlen( $nodePathString ) == 0 )
1127             $nodePathString = '/';
1128
1129         $nodePathString = eZURLAliasML::convertPathToAlias( $nodePathString );
1130
1131         // Attempt to translate the URL to something like "/content/view/full/84".
1132         $translateResult = eZURLAliasML::translate( $nodePathString );
1133
1134         // handle redirects
1135         while ( $nodePathString == 'error/301' )
1136         {
1137             $nodePathString = $translateResult;
1138
1139             $translateResult = eZURLAliasML::translate( $nodePathString );
1140         }
1141
1142         // Get the ID of the node (which is the last part of the translated path).
1143         if ( preg_match( "#^content/view/full/([0-9]+)$#", $nodePathString, $matches ) )
1144         {
1145             $nodeID = $matches[1];
1146             $this->appendLogEntry( "NodeID: $nodeID", 'CS:fetchParentNodeByTranslation' );
1147         }
1148         else
1149         {
1150             $this->appendLogEntry( "Root node", 'CS:fetchParentNodeByTranslation' );
1151             $nodeID = 2;
1152         }
1153
1154         // Attempt to fetch the node.
1155         $node = eZContentObjectTreeNode::fetch( $nodeID );
1156
1157         // Return the node.
1158         return $node;
1159     }
1160
1161     /*!
1162      \return An array containing the names of all folders in the virtual root.
1163     */
1164     function virtualFolderList()
1165     {
1166         return array( eZWebDAVContentServer::virtualContentFolderName(), eZWebDAVContentServer::virtualMediaFolderName() );
1167     }
1168
1169     /*!
1170      \return An array containing the names of all folders in the virtual root.
1171     */
1172     function virtualFolderInfoList()
1173     {
1174         return array( array( 'name' => eZWebDAVContentServer::virtualContentFolderName() ),
1175                       array( 'name' => eZWebDAVContentServer::virtualMediaFolderName() ) );
1176     }
1177
1178     /*!
1179      \return An array containing the names of all files in the virtual root.
1180     */
1181     function virtualFileList()
1182     {
1183         return array( basename( eZWebDAVContentServer::virtualInfoFileName() ) );
1184     }
1185
1186     /*!
1187      \return An array containing the names of all files in the virtual root.
1188     */
1189     function virtualFileInfoList()
1190     {
1191         return array( array( 'name' => basename( eZWebDAVContentServer::virtualInfoFileName() ),
1192                              'filepath' => eZWebDAVContentServer::virtualInfoFileName() ) );
1193     }
1194
1195     /*!
1196      \return An array containing the names of all elements in the virtual root.
1197     */
1198     function virtualList()
1199     {
1200         return array_merge( eZWebDAVContentServer::virtualFolderList(),
1201                             eZWebDAVContentServer::virtualFileList() );
1202     }
1203
1204     /*!
1205      \return An array containing the names of all elements in the virtual root.
1206     */
1207     function virtualInfoList()
1208     {
1209         return array_merge( eZWebDAVContentServer::virtualFolderInfoList(),
1210                             eZWebDAVContentServer::virtualFileInfoList() );
1211     }
1212
1213     /*!
1214      Functions related to creation collections
1215      @{
1216     */
1217
1218     /*!
1219       Builds and returns the content of the virtual start fodler
1220       for a site. The virtual startfolder is an intermediate step
1221       between the site-list and actual content. This directory
1222       contains the "content" folder which leads to the site's
1223       actual content.
1224     */
1225     function fetchVirtualSiteContent( $site, $depth, $properties )
1226     {
1227         $this->appendLogEntry( "Script URL.." . eZSys::instance()->RequestURI, 'CS:fetchVirtualSiteContent' );
1228         // Location of the info file.
1229         $infoFile = $_SERVER['DOCUMENT_ROOT'] . '/' . eZWebDAVContentServer::virtualInfoFileName();
1230
1231         // Always add the current collection
1232         $contentEntry = array();
1233         $contentEntry["name"]     = eZSys::instance()->RequestURI;
1234         $contentEntry["size"]     = 0;
1235         $contentEntry["mimetype"] = 'httpd/unix-directory';
1236         $contentEntry["ctime"]    = filectime( 'settings/siteaccess/' . $site );
1237         $contentEntry["mtime"]    = filemtime( 'settings/siteaccess/' . $site );
1238         $contentEntry["href"]     = eZSys::instance()->RequestURI;
1239         $entries[] = $contentEntry;
1240
1241         $defctime = $contentEntry['ctime'];
1242         $defmtime = $contentEntry['mtime'];
1243
1244         if ( $depth > 0 )
1245         {
1246             $scriptURL = eZSys::instance()->RequestURI;
1247             if ( $scriptURL{strlen($scriptURL) - 1} != "/" )
1248                 $scriptURL .= "/";
1249
1250             // Set up attributes for the virtual content folder:
1251             foreach ( $this->virtualInfoList() as $info )
1252             {
1253                 $name = $info['name'];
1254                 $filepath = false;
1255                 if ( isset( $info['filepath'] ) )
1256                     $filepath = $info['filepath'];
1257                 $size = 0;
1258                 if ( $filepath === false or file_exists( $filepath ) )
1259                 {
1260                     $mimeType = 'httpd/unix-directory';
1261                     if ( $filepath !== false )
1262                     {
1263                         $mimeInfo = eZMimeType::findByFileContents( $filepath );
1264                         $mimeType = $mimeInfo['name'];
1265                         $ctime = filectime( $filepath );
1266                         $mtime = filemtime( $filepath );
1267                         $size  = filesize( $filepath );
1268                     }
1269                     else
1270                     {
1271                         $ctime = $defctime;
1272                         $mtime = $defmtime;
1273                     }
1274
1275                     $entry             = array();
1276                     $entry["name"]     = $name;
1277                     $entry["size"]     = $size;
1278                     $entry["mimetype"] = $mimeType;
1279                     $entry["ctime"]    = $ctime;
1280                     $entry["mtime"]    = $mtime;
1281                     $entry["href"]     = $scriptURL . $name;
1282                     $entries[]         = $entry;
1283                 }
1284             }
1285         }
1286
1287         return $entries;
1288     }
1289
1290     /*!
1291       Builds a content-list of available sites and returns it.
1292     */
1293     function fetchSiteListContent( $depth, $properties )
1294     {
1295         // At the end: we'll return an array of entry-arrays.
1296         $entries = array();
1297
1298         // An entry consists of several attributes (name, size, etc).
1299         $contentEntry = array();
1300         $entries = array();
1301
1302         // Set up attributes for the virtual site-list folder:
1303         $contentEntry["name"]     = '/';
1304         $contentEntry["href"]     = eZSys::instance()->RequestURI;
1305         $contentEntry["size"]     = 0;
1306         $contentEntry["mimetype"] = 'httpd/unix-directory';
1307         $contentEntry["ctime"]    = filectime( 'var' );
1308         $contentEntry["mtime"]    = filemtime( 'var' );
1309
1310         $entries[] = $contentEntry;
1311
1312         if ( $depth > 0 )
1313         {
1314             // Get list of available sites.
1315             $sites = $this->availableSites();
1316
1317             // For all available sites:
1318             foreach ( $sites as $site )
1319             {
1320                 // Set up attributes for the virtual site-list folder:
1321                 $contentEntry["name"]     = eZSys::instance()->RequestURI . $site;
1322                 $contentEntry["size"]     = 0;
1323                 $contentEntry["mimetype"] = 'httpd/unix-directory';
1324                 $contentEntry["ctime"]    = filectime( 'settings/siteaccess/' . $site );
1325                 $contentEntry["mtime"]    = filemtime( 'settings/siteaccess/' . $site );
1326
1327                 if ( eZSys::instance()->RequestURI == '/' )
1328                 {
1329                     $contentEntry["href"] = $contentEntry["name"];
1330                 }
1331                 else
1332                 {
1333                     $contentEntry["href"] = eZSys::instance()->RequestURI . $contentEntry["name"];
1334                 }
1335
1336                 $entries[] = $contentEntry;
1337             }
1338         }
1339
1340         return $entries;
1341     }
1342
1343     /*!
1344       Gets and returns the content of an actual node.
1345       List of other nodes belonging to the target node
1346       (one level below it) will be returned.
1347     */
1348     function fetchContentList( &$node, $target, $depth, $properties )
1349     {
1350         // We'll return an array of entries (which is an array of attributes).
1351         $entries = array();
1352
1353         if ( $depth == 1 )
1354         {
1355             // Get all the children of the target node.
1356             $subTree = $node->subTree( array ( 'Depth' => 1 ) );
1357
1358             // Build the entries array by going through all the
1359             // nodes in the subtree and getting their attributes:
1360             foreach ( $subTree as $someNode )
1361             {
1362                 $entries[] = $this->fetchNodeInfo( $someNode );
1363             }
1364         }
1365
1366         // Always include the information about the current level node
1367         $thisNodeInfo = array();
1368         $thisNodeInfo = $this->fetchNodeInfo( $node );
1369         $thisNodeInfo["href"] = eZSys::instance()->RequestURI;
1370         $entries[] = $thisNodeInfo;
1371
1372         // Return the content of the target.
1373         return $entries;
1374     }
1375
1376     /*!
1377       \return \c true if the object \a $object should always be considered a folder.
1378     */
1379     function isObjectFolder( $object, &$class )
1380     {
1381         $classIdentifier = $class->attribute( 'identifier' );
1382         if ( $this->FolderClasses === null )
1383         {
1384             $webdavINI = eZINI::instance( eZWebDAVContentServer::WEBDAV_INI_FILE );
1385             $folderClasses = array();
1386             if ( $webdavINI->hasGroup( 'GeneralSettings' ) and
1387                  $webdavINI->hasVariable( 'GeneralSettings', 'FolderClasses' ) )
1388             {
1389                 $folderClasses = $webdavINI->variable( 'GeneralSettings', 'FolderClasses' );
1390             }
1391             $this->FolderClasses = $folderClasses;
1392         }
1393         return in_array( $classIdentifier, $this->FolderClasses );
1394     }
1395
1396     /*!
1397       Gathers information about a given node (specified as parameter).
1398     */
1399     function fetchNodeInfo( &$node )
1400     {
1401         // When finished, we'll return an array of attributes/properties.
1402         $entry = array();
1403
1404         // Grab settings from the ini file:
1405         $webdavINI = eZINI::instance( eZWebDAVContentServer::WEBDAV_INI_FILE );
1406         $iniSettings = $webdavINI->variable( 'DisplaySettings', 'FileAttribute' );
1407
1408         $classIdentifier = $node->attribute( 'class_identifier' );
1409
1410         $object = $node->attribute( 'object' );
1411
1412         // By default, everything is displayed as a folder:
1413         // Trim the name of the node, it is in some cases whitespace in eZ Publish
1414         $entry["name"] = trim( $node->attribute( 'name' ) );
1415         $entry["size"] = 0;
1416         $entry["mimetype"] = 'httpd/unix-directory';
1417         $entry["ctime"] = $object->attribute( 'published' );
1418         $entry["mtime"] = $object->attribute( 'modified' );
1419
1420         $upload = new eZContentUpload();
1421         $info = $upload->objectFileInfo( $object );
1422         $suffix = '';
1423         $class = $object->contentClass();
1424         $isObjectFolder = $this->isObjectFolder( $object, $class );
1425
1426         if ( $isObjectFolder )
1427         {
1428             // We do nothing, the default is to see it as a folder
1429         }
1430         else if ( $info )
1431         {
1432             $filePath = $info['filepath'];
1433             $entry["mimetype"] = false;
1434             $entry["size"] = false;
1435             if ( isset( $info['filesize'] ) )
1436                 $entry['size'] = $info['filesize'];
1437             if ( isset( $info['mime_type'] ) )
1438                 $entry['mimetype'] = $info['mime_type'];
1439
1440             // Fill in information from the actual file if they are missing.
1441             $file = eZClusterFileHandler::instance( $filePath );
1442             if ( !$entry['size'] and $file->exists() )
1443             {
1444                 $entry["size"] = $file->size();
1445             }
1446             if ( !$entry['mimetype']  )
1447             {
1448                 $mimeInfo = eZMimeType::findByURL( $filePath );
1449                 $entry["mimetype"] = $mimeInfo['name'];
1450                 $suffix = $mimeInfo['suffix'];
1451                 if ( strlen( $suffix ) > 0 )
1452                     $entry["name"] .= '.' . $suffix;
1453             }
1454             else
1455             {
1456                 // eZMimeType returns first suffix in its list
1457                 // this could be another one than the original file extension
1458                 // so let's try to get the suffix from the file path first
1459                 $suffix = eZFile::suffix( $filePath );
1460                 if ( !$suffix )
1461                 {
1462                     $mimeInfo = eZMimeType::findByName( $entry['mimetype'] );
1463                     $suffix = $mimeInfo['suffix'];
1464                 }
1465                 if ( strlen( $suffix ) > 0 )
1466                     $entry["name"] .= '.' . $suffix;
1467             }
1468
1469             if ( $file->exists() )
1470             {
1471                 $entry["ctime"] = $file->mtime();
1472                 $entry["mtime"] = $file->mtime();
1473             }
1474         }
1475         else
1476         {
1477             // Here we only show items as folders if they have
1478             // is_container set to true, otherwise it's an unknown binary file
1479             if ( !$class->attribute( 'is_container' ) )
1480             {
1481                 $entry['mimetype'] = 'application/octet-stream';
1482             }
1483         }
1484
1485         $scriptURL = eZSys::instance()->RequestURI;
1486         if ( strlen( $scriptURL ) > 0 and $scriptURL[ strlen( $scriptURL ) - 1 ] != "/" )
1487             $scriptURL .= "/";
1488
1489         $trimmedScriptURL = trim( $scriptURL, '/' );
1490         $scriptURLParts = explode( '/', $trimmedScriptURL );
1491
1492         $siteAccess = $scriptURLParts[0];
1493         $virtualFolder = $scriptURLParts[1];
1494
1495         $startURL = '/' . $siteAccess . '/' . $virtualFolder . '/';
1496
1497         // Set the href attribute (note that it doesn't just equal the name).
1498         if ( !isset( $entry['href'] ) )
1499         {
1500             if ( strlen( $suffix ) > 0 )
1501                 $suffix = '.' . $suffix;
1502
1503             $alias = $node->urlAlias();
1504             if ( $virtualFolder == eZWebDAVContentServer::virtualMediaFolderName() )
1505             {
1506                 // remove the real media node url alias, the virtual media folder is already in $startURL
1507                 $aliasParts = explode( '/', $alias );
1508                 array_shift( $aliasParts );
1509                 $alias = implode( '/', $aliasParts );
1510             }
1511             $entry["href"] = $startURL . $alias . $suffix;
1512         }
1513         // Return array of attributes/properties (name, size, mime, times, etc.).
1514         return $entry;
1515     }
1516
1517     /*!
1518      @}
1519     */
1520
1521     /*!
1522       Creates a new folder under the given target node.
1523     */
1524     function createFolder( $parentNode, $target )
1525     {
1526         // Grab settings from the ini file:
1527         $webdavINI = eZINI::instance( eZWebDAVContentServer::WEBDAV_INI_FILE );
1528         $folderClassID = $webdavINI->variable( 'FolderSettings', 'FolderClass' );
1529         $languageCode = eZContentObject::defaultLanguage();
1530
1531         $contentObject = eZContentObject::createWithNodeAssignment( $parentNode, $folderClassID, $languageCode );
1532         if ( $contentObject )
1533         {
1534             $db = eZDB::instance();
1535             $db->begin();
1536             $version = $contentObject->version( 1 );
1537             $version->setAttribute( 'status', eZContentObjectVersion::STATUS_DRAFT );
1538             $version->store();
1539
1540             $contentObjectID = $contentObject->attribute( 'id' );
1541             $contentObjectAttributes = $version->contentObjectAttributes();
1542
1543             $contentObjectAttributes[0]->setAttribute( 'data_text', basename( $target ) );
1544             $contentObjectAttributes[0]->store();
1545             $db->commit();
1546
1547             $operationResult = eZOperationHandler::execute( 'content', 'publish', array( 'object_id' => $contentObjectID,
1548                                                                                          'version' => 1 ) );
1549             return eZWebDAVServer::OK_CREATED;
1550         }
1551         else
1552         {
1553             $this->appendLogEntry( "Not allowed", 'CS:createFolder' );
1554             return eZWebDAVServer::FAILED_FORBIDDEN;
1555         }
1556     }
1557
1558     /*!
1559       Gets and returns a list of the available sites (from site.ini).
1560     */
1561     function availableSites()
1562     {
1563         // The site list is an array of strings.
1564         $siteList = array();
1565
1566         // Grab the sitelist from the ini file.
1567         $webdavINI = eZINI::instance();
1568         $siteList = $webdavINI->variable( 'SiteSettings', 'SiteList' );
1569
1570         // Return the site list.
1571         return $siteList ;
1572     }
1573
1574     static function virtualContentFolderName()
1575     {
1576         return ezpI18n::tr( 'kernel/content', "Content" );
1577     }
1578
1579     static function virtualMediaFolderName()
1580     {
1581         return ezpI18n::tr( 'kernel/content', "Media" );
1582     }
1583
1584     static function virtualInfoFileName()
1585     {
1586         $infoFile = eZSys::varDirectory() . '/webdav/root/info.txt';
1587         return $infoFile;
1588     }
1589
1590     /// \privatesection
1591     /// Contains an array with classes that are considered folder
1592     public $FolderClasses;
1593 }
1594 ?>