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.commons.lang3.StringUtils;
033import org.apache.excalibur.source.Source;
034import org.apache.excalibur.source.SourceException;
035import org.apache.excalibur.source.SourceResolver;
036import org.apache.excalibur.source.TraversableSource;
037import org.xml.sax.SAXException;
038
039import org.ametys.core.user.CurrentUserProvider;
040import org.ametys.core.util.URIUtils;
041import org.ametys.plugins.repository.AmetysRepositoryException;
042import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
043import org.ametys.runtime.authentication.AccessDeniedException;
044import org.ametys.runtime.authentication.AuthorizationRequiredException;
045import org.ametys.web.repository.page.ZoneItem;
046
047/**
048 * Generate all subDirectory from a directory
049 */
050public class ServerDirectoryGenerator extends ServiceableGenerator
051{
052    /** Excalibur's source resolver */
053    protected SourceResolver _sourceResolver;
054    
055    /** The current user provider */
056    protected CurrentUserProvider _currentUserProvider;
057
058    @Override
059    public void service(ServiceManager smanager) throws ServiceException
060    {
061        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
062        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
063    }
064    
065    public void generate() throws IOException, SAXException, ProcessingException
066    {
067        Request request = ObjectModelHelper.getRequest(objectModel);
068        
069        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
070        ModelAwareDataHolder serviceParameters = zoneItem.getServiceParameters();
071        String folder = ServerDirectoryHelper.normalize(serviceParameters.getValueOrDefault("folder", StringUtils.EMPTY));
072        
073        try
074        {
075            contentHandler.startDocument();
076            
077            if (serviceParameters.getValueOrDefault("enableDynamicPaths", false))
078            {
079                folder = ServerDirectoryHelper.evaluateDynamicPath(folder, (String) request.getAttribute("site"), (String) request.getAttribute("sitemapLanguage"), _currentUserProvider.getUser());
080            }
081            
082            Set<Source> rootSources = ServerDirectoryHelper.getRootServerSources(_sourceResolver);
083            
084            if (!ServerDirectoryHelper.isValidPath(folder, rootSources))
085            {
086                throw new IllegalStateException("The server directory '" + folder + "' is not an authorized directory");
087            }
088            
089            Source sourceFolder = null;
090            try
091            {
092                sourceFolder = _sourceResolver.resolveURI(folder);
093                
094                if (!sourceFolder.exists())
095                {
096                    throw new IllegalArgumentException("The server directory '" + folder + "' does not exist");
097                }
098                
099                String zoneItemEncoded = new String(Base64.getUrlEncoder().encode(zoneItem.getId().getBytes("UTF-8")));
100                Long depth = serviceParameters.getValue("depth");
101                saxCollection(sourceFolder, zoneItemEncoded, depth == null || depth.longValue() != depth.intValue() ? Integer.MAX_VALUE : depth.intValue());
102            }
103            catch (SourceException | AmetysRepositoryException | URISyntaxException e)
104            {
105                throw new IllegalArgumentException("Cannot enumerate subdirectories for server location: <" + folder + ">", e);
106            }
107            finally
108            {
109                _sourceResolver.release(sourceFolder);
110            }
111            
112            contentHandler.endDocument();
113        }
114        catch (AccessDeniedException | AuthorizationRequiredException e)
115        {
116            throw new ProcessingException("No user is connected or the user has no required permissions to access to the server directory " + folder);
117        }
118    }
119    
120    /**
121     * SAX a {@link Source}
122     * @param sourceFolder The source to sax
123     * @param zoneItemId The zone item id
124     * @param depth remaining max depth
125     * @throws SAXException If an error occurred while saxing
126     * @throws SourceException If an error occurred while we get child from the source
127     * @throws URISyntaxException if an error occurred 
128     */
129    protected void saxCollection (Source sourceFolder, String zoneItemId, Integer depth) throws SAXException, SourceException, URISyntaxException
130    {
131        if (sourceFolder instanceof TraversableSource)
132        {            
133            TraversableSource tSource = (TraversableSource) sourceFolder;
134            
135            AttributesImpl childAtts = new AttributesImpl();
136            childAtts.addCDATAAttribute("id", tSource.getURI());
137            childAtts.addCDATAAttribute("name", tSource.getName());
138            childAtts.addCDATAAttribute("encodedName", URIUtils.encodePathSegment(tSource.getName()));
139            childAtts.addCDATAAttribute("mimetype", tSource.getMimeType());
140            childAtts.addCDATAAttribute("lastModified", String.valueOf(tSource.getLastModified()));
141            childAtts.addCDATAAttribute("size", String.valueOf(tSource.getContentLength()));
142            childAtts.addCDATAAttribute("path", URIUtils.encodePath(tSource.getURI()));
143            
144            if (tSource.isCollection())
145            {
146                childAtts.addCDATAAttribute("type", "collection");
147                XMLUtils.startElement(contentHandler, "Node", childAtts);
148
149                if (depth > 0)
150                {
151                    Collection<Source> childrenSources = tSource.getChildren(); 
152                    for (Source childSource : childrenSources)
153                    {
154                        saxCollection(childSource, zoneItemId, depth - 1);
155                    }
156                }
157                XMLUtils.endElement(contentHandler, "Node");
158            }
159            else
160            {
161                childAtts.addCDATAAttribute("type", "resource");
162                childAtts.addCDATAAttribute("zoneItem", zoneItemId);
163                XMLUtils.createElement(contentHandler, "Node", childAtts);
164            }
165        }
166    }
167}