001/* 002 * Copyright 2010 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.explorer.resources.readers; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.io.Serializable; 021import java.io.UnsupportedEncodingException; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.net.URLDecoder; 025import java.util.Map; 026 027import org.apache.avalon.framework.parameters.Parameters; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.cocoon.ProcessingException; 032import org.apache.cocoon.ResourceNotFoundException; 033import org.apache.cocoon.caching.CacheableProcessingComponent; 034import org.apache.cocoon.environment.SourceResolver; 035import org.apache.commons.lang.StringUtils; 036import org.apache.excalibur.source.SourceValidity; 037import org.apache.excalibur.source.impl.validity.TimeStampValidity; 038import org.xml.sax.SAXException; 039 040import org.ametys.core.right.RightManager; 041import org.ametys.core.user.CurrentUserProvider; 042import org.ametys.core.user.UserIdentity; 043import org.ametys.core.util.cocoon.AbstractResourceReader; 044import org.ametys.plugins.explorer.resources.Resource; 045import org.ametys.plugins.repository.AmetysObjectResolver; 046import org.ametys.plugins.repository.UnknownAmetysObjectException; 047import org.ametys.plugins.repository.version.VersionableAmetysObject; 048import org.ametys.runtime.authentication.AccessDeniedException; 049import org.ametys.runtime.authentication.AuthorizationRequiredException; 050 051/** 052 * Reader for {@link Resource} 053 */ 054public class AmetysResourceReader extends AbstractResourceReader implements Serviceable, CacheableProcessingComponent 055{ 056 /** The Ametys object resolver */ 057 protected AmetysObjectResolver _resolver; 058 /** The resource */ 059 protected Resource _object; 060 /** The right manager */ 061 protected RightManager _rightManager; 062 /** The current user provider */ 063 protected CurrentUserProvider _currentUserProvider; 064 065 @Override 066 public void service(ServiceManager sManager) throws ServiceException 067 { 068 _resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE); 069 _rightManager = (RightManager) sManager.lookup(RightManager.ROLE); 070 _currentUserProvider = (CurrentUserProvider) sManager.lookup(CurrentUserProvider.ROLE); 071 } 072 073 @Override 074 protected void doSetup(SourceResolver res, Map objModel, String src, Parameters par) throws ProcessingException, IOException 075 { 076 String id = par.getParameter("id", null); 077 String path = par.getParameter("path", null); 078 String version = par.getParameter("version", null); 079 080 try 081 { 082 if (id != null) 083 { 084 _object = _resolver.resolveById(id); 085 } 086 else 087 { 088 _object = _resolver.resolveByPath(_decodePath(path.substring(1))); 089 } 090 091 // Check user access 092 checkUserAccess(); 093 094 if (!StringUtils.isEmpty(version) && _object instanceof VersionableAmetysObject) 095 { 096 ((VersionableAmetysObject) _object).switchToRevision(version); 097 } 098 } 099 catch (UnknownAmetysObjectException e) 100 { 101 if (id != null) 102 { 103 throw new ResourceNotFoundException(String.format("The resource with id '%s' does not exist", id)); 104 } 105 else 106 { 107 throw new ResourceNotFoundException(String.format("The resource at path '%s' does not exist", path)); 108 } 109 } 110 } 111 112 @Override 113 protected InputStream getInputStream() 114 { 115 return _object.getInputStream(); 116 } 117 118 @Override 119 protected String getFilename() 120 { 121 String name = _object.getName(); 122 name = name.replaceAll("\\\\", "\\\\\\\\"); 123 name = name.replaceAll("\\\"", "\\\\\\\""); 124 125 return name; 126 } 127 128 @Override 129 protected String getEncodedFilename() 130 { 131 String name = _object.getName(); 132 String encodedName = null; 133 try 134 { 135 URI uri = new URI(null, null, name, null); 136 encodedName = uri.toASCIIString(); 137 // EXPLORER-358 : Fix for Chrome which does not support comma in filename 138 encodedName = encodedName.replaceAll(",", "%2C"); 139 } 140 catch (URISyntaxException e) 141 { 142 // do nothing and send no encoded name 143 } 144 145 return encodedName; 146 } 147 148 @Override 149 protected long getLength() 150 { 151 return _object.getLength(); 152 } 153 154 /** 155 * Check the user access 156 * @throws AuthorizationRequiredException if authorization is required 157 * @throws AccessDeniedException if user has no access 158 */ 159 protected void checkUserAccess() throws AuthorizationRequiredException, AccessDeniedException 160 { 161 UserIdentity user = _currentUserProvider.getUser(); 162 163 if (user == null) 164 { 165 // Check anonymous access 166 if (!_rightManager.hasAnonymousReadAccess(_object.getParent())) 167 { 168 throw new AuthorizationRequiredException(null); 169 } 170 } 171 else if (!_rightManager.hasReadAccess(user, _object.getParent())) 172 { 173 throw new AccessDeniedException("User " + user + " has no right to access the resource " + _object.getId()); 174 } 175 } 176 177 /** 178 * Decode the resource path 179 * @param path the resource path 180 * @return the decoded resource path 181 * @throws UnsupportedEncodingException if UTF-8 encoding is not supported 182 */ 183 protected String _decodePath (String path) throws UnsupportedEncodingException 184 { 185 StringBuffer sb = new StringBuffer(); 186 187 String[] parts = path.split("/"); 188 for (String part : parts) 189 { 190 sb.append("/"); 191 sb.append(URLDecoder.decode(part, "utf-8")); 192 } 193 return sb.toString(); 194 } 195 196 @Override 197 public Serializable getKey() 198 { 199 return _object.getId() + getKeySuffix(); 200 } 201 202 @Override 203 public SourceValidity getValidity() 204 { 205 return new TimeStampValidity(getLastModified()); 206 } 207 208 @Override 209 public long getLastModified() 210 { 211 return _object.getLastModified().getTime(); 212 } 213 214 @Override 215 public String getMimeType() 216 { 217 return _object.getMimeType(); 218 } 219 220 @Override 221 public void recycle() 222 { 223 super.recycle(); 224 _object = null; 225 } 226}