001/*
002 *  Copyright 2015 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.serverdirectory;
017
018import java.io.IOException;
019import java.net.URISyntaxException;
020import java.util.Base64;
021import java.util.Collection;
022import java.util.Set;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.ProcessingException;
027import org.apache.cocoon.environment.ObjectModelHelper;
028import org.apache.cocoon.environment.Request;
029import org.apache.cocoon.generation.ServiceableGenerator;
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.excalibur.source.Source;
033import org.apache.excalibur.source.SourceException;
034import org.apache.excalibur.source.SourceResolver;
035import org.apache.excalibur.source.TraversableSource;
036import org.xml.sax.SAXException;
037
038import org.ametys.core.user.CurrentUserProvider;
039import org.ametys.core.util.URIUtils;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
042import org.ametys.runtime.authentication.AccessDeniedException;
043import org.ametys.runtime.authentication.AuthorizationRequiredException;
044import org.ametys.web.repository.page.ZoneItem;
045
046/**
047 * Generate all subDirectory from a directory
048 */
049public class ServerDirectoryGenerator extends ServiceableGenerator
050{
051    /** Excalibur's source resolver */
052    protected SourceResolver _sourceResolver;
053    
054    /** The current user provider */
055    protected CurrentUserProvider _currentUserProvider;
056
057    @Override
058    public void service(ServiceManager smanager) throws ServiceException
059    {
060        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
061        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
062    }
063    
064    public void generate() throws IOException, SAXException, ProcessingException
065    {
066        Request request = ObjectModelHelper.getRequest(objectModel);
067        
068        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
069        ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
070        String folder = ServerDirectoryHelper.normalize(serviceParameters.getValue("folder", false, ""));
071        
072        try
073        {
074            contentHandler.startDocument();
075            
076            if (serviceParameters.getValue("enableDynamicPaths", false, false))
077            {
078                folder = ServerDirectoryHelper.evaluateDynamicPath(folder, (String) request.getAttribute("site"), (String) request.getAttribute("sitemapLanguage"), _currentUserProvider.getUser());
079            }
080            
081            Set<Source> rootSources = ServerDirectoryHelper.getRootServerSources(_sourceResolver);
082            
083            if (!ServerDirectoryHelper.isValidPath(folder, rootSources))
084            {
085                throw new IllegalStateException("The server directory '" + folder + "' is not an authorized directory");
086            }
087            
088            Source sourceFolder = null;
089            try
090            {
091                sourceFolder = _sourceResolver.resolveURI(folder);
092                
093                if (!sourceFolder.exists())
094                {
095                    throw new IllegalArgumentException("The server directory '" + folder + "' does not exist");
096                }
097                
098                String zoneItemEncoded = new String(Base64.getUrlEncoder().encode(zoneItem.getId().getBytes("UTF-8")));
099                Long depth = serviceParameters.getValue("depth");
100                saxCollection(sourceFolder, zoneItemEncoded, depth == null || depth.longValue() != depth.intValue() ? Integer.MAX_VALUE : depth.intValue());
101            }
102            catch (SourceException | AmetysRepositoryException | URISyntaxException e)
103            {
104                throw new IllegalArgumentException("Cannot enumerate subdirectories for server location: <" + folder + ">", e);
105            }
106            finally
107            {
108                _sourceResolver.release(sourceFolder);
109            }
110            
111            contentHandler.endDocument();
112        }
113        catch (AccessDeniedException | AuthorizationRequiredException e)
114        {
115            throw new ProcessingException("No user is connected or the user has no required permissions to access to the server directory " + folder);
116        }
117    }
118    
119    /**
120     * SAX a {@link Source}
121     * @param sourceFolder The source to sax
122     * @param zoneItemId The zone item id
123     * @param depth remaining max depth
124     * @throws SAXException If an error occurred while saxing
125     * @throws SourceException If an error occurred while we get child from the source
126     * @throws URISyntaxException if an error occurred 
127     */
128    protected void saxCollection (Source sourceFolder, String zoneItemId, Integer depth) throws SAXException, SourceException, URISyntaxException
129    {
130        if (sourceFolder instanceof TraversableSource)
131        {            
132            TraversableSource tSource = (TraversableSource) sourceFolder;
133            
134            AttributesImpl childAtts = new AttributesImpl();
135            childAtts.addCDATAAttribute("id", tSource.getURI());
136            childAtts.addCDATAAttribute("name", tSource.getName());
137            childAtts.addCDATAAttribute("encodedName", URIUtils.encodePathSegment(tSource.getName()));
138            childAtts.addCDATAAttribute("mimetype", tSource.getMimeType());
139            childAtts.addCDATAAttribute("lastModified", String.valueOf(tSource.getLastModified()));
140            childAtts.addCDATAAttribute("size", String.valueOf(tSource.getContentLength()));
141            childAtts.addCDATAAttribute("path", URIUtils.encodePath(tSource.getURI()));
142            
143            if (tSource.isCollection())
144            {
145                childAtts.addCDATAAttribute("type", "collection");
146                XMLUtils.startElement(contentHandler, "Node", childAtts);
147
148                if (depth > 0)
149                {
150                    Collection<Source> childrenSources = tSource.getChildren(); 
151                    for (Source childSource : childrenSources)
152                    {
153                        saxCollection(childSource, zoneItemId, depth - 1);
154                    }
155                }
156                XMLUtils.endElement(contentHandler, "Node");
157            }
158            else
159            {
160                childAtts.addCDATAAttribute("type", "resource");
161                childAtts.addCDATAAttribute("zoneItem", zoneItemId);
162                XMLUtils.createElement(contentHandler, "Node", childAtts);
163            }
164        }
165    }
166}