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.URLEncoder;
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                saxCollection(sourceFolder, zoneItemEncoded);
100            }
101            catch (SourceException | AmetysRepositoryException | URISyntaxException e)
102            {
103                throw new IllegalArgumentException("Cannot enumerate subdirectories for server location: <" + folder + ">", e);
104            }
105            finally
106            {
107                _sourceResolver.release(sourceFolder);
108            }
109            
110            contentHandler.endDocument();
111        }
112        catch (AccessDeniedException | AuthorizationRequiredException e)
113        {
114            throw new ProcessingException("No user is connected or the user has no required permissions to access to the server directory " + folder);
115        }
116    }
117    
118    /**
119     * SAX a {@link Source}
120     * @param sourceFolder The source to sax
121     * @param zoneItemId The zone item id
122     * @throws SAXException If an error occurred while saxing
123     * @throws SourceException If an error occurred while we get child from the source
124     * @throws URISyntaxException if an error occurred 
125     */
126    protected void saxCollection (Source sourceFolder, String zoneItemId) throws SAXException, SourceException, URISyntaxException
127    {
128        if (sourceFolder instanceof TraversableSource)
129        {            
130            TraversableSource tSource = (TraversableSource) sourceFolder;
131            
132            AttributesImpl childAtts = new AttributesImpl();
133            childAtts.addCDATAAttribute("id", tSource.getURI());
134            childAtts.addCDATAAttribute("name", tSource.getName());
135            childAtts.addCDATAAttribute("encodedName", URLEncoder.encodePath(tSource.getName()));
136            childAtts.addCDATAAttribute("mimetype", tSource.getMimeType());
137            childAtts.addCDATAAttribute("lastModified", String.valueOf(tSource.getLastModified()));
138            childAtts.addCDATAAttribute("size", String.valueOf(tSource.getContentLength()));
139            childAtts.addCDATAAttribute("path", URLEncoder.encodePath(tSource.getURI()));
140            
141            if (tSource.isCollection())
142            {
143                childAtts.addCDATAAttribute("type", "collection");
144                XMLUtils.startElement(contentHandler, "Node", childAtts);
145
146                Collection<Source> childrenSources = tSource.getChildren(); 
147                for (Source childSource : childrenSources)
148                {
149                    saxCollection(childSource, zoneItemId);
150                }
151                XMLUtils.endElement(contentHandler, "Node");
152            }
153            else
154            {
155                childAtts.addCDATAAttribute("type", "resource");
156                childAtts.addCDATAAttribute("zoneItem", zoneItemId);
157                XMLUtils.createElement(contentHandler, "Node", childAtts);
158            }
159            
160        }
161    }
162}