001/*
002 *  Copyright 2017 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.cms.remote;
017
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.Map;
021import java.util.regex.Matcher;
022import java.util.regex.Pattern;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.cocoon.components.ContextHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.commons.lang.StringUtils;
033
034import org.ametys.cms.transformation.ConsistencyChecker.CHECK;
035import org.ametys.cms.transformation.URIResolver;
036import org.ametys.cms.transformation.URIResolverExtensionPoint;
037import org.ametys.cms.transformation.xslt.ResolveURIComponent;
038import org.ametys.runtime.config.Config;
039import org.ametys.runtime.i18n.I18nizableText;
040import org.ametys.runtime.workspace.WorkspaceMatcher;
041
042
043/**
044 * {@link URIResolver} for type "remote".<br>
045 * This resolver is a proxy to another type to be called from remote url.
046 * The resolved uri are necessary absolutes.
047 */
048public class RemoteURIResolver implements URIResolver, Serviceable, Contextualizable
049{
050    private static final Pattern __URI_PATTERN = Pattern.compile("([^;]*);(.*)");
051    
052    /** The context */
053    protected Context _context;
054    
055    /** The ametys uri resolver */
056    protected URIResolverExtensionPoint _uriResolverEP;
057    
058    @Override
059    public void service(ServiceManager manager) throws ServiceException
060    {
061        _uriResolverEP = (URIResolverExtensionPoint) manager.lookup(URIResolverExtensionPoint.ROLE);
062    }
063    
064    @Override
065    public void contextualize(Context context) throws ContextException
066    {
067        _context = context;
068    }
069    
070    @Override
071    public String getType()
072    {
073        return "remote";
074    }
075
076    @Override
077    public String resolve(String uri, boolean download, boolean absolute, boolean internal)
078    {
079        Request request = ContextHelper.getRequest(_context);
080        
081        ProxiedUri proxiedUri = getProxiedUri(uri);
082        
083        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolve(proxiedUri.getType(), proxiedUri.getUri(), download, false, false));
084    }
085
086    @Override
087    public String resolveImage(String uri, int height, int width, boolean download, boolean absolute, boolean internal)
088    {
089        Request request = ContextHelper.getRequest(_context);
090        
091        ProxiedUri proxiedUri = getProxiedUri(uri);
092        
093        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveImage(proxiedUri.getType(), proxiedUri.getUri(), height, width, download, false, false));
094    }
095
096    @Override
097    public String resolveImageAsBase64(String uri, int height, int width)
098    {
099        Request request = ContextHelper.getRequest(_context);
100        
101        ProxiedUri proxiedUri = getProxiedUri(uri);
102        
103        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveImage(proxiedUri.getType(), proxiedUri.getUri(), height, width));
104    }
105
106    @Override
107    public String resolveBoundedImage(String uri, int maxHeight, int maxWidth, boolean download, boolean absolute, boolean internal)
108    {
109        Request request = ContextHelper.getRequest(_context);
110        
111        ProxiedUri proxiedUri = getProxiedUri(uri);
112        
113        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveBoundedImage(proxiedUri.getType(), proxiedUri.getUri(), maxHeight, maxWidth, download, false, false));
114    }
115
116    @Override
117    public String resolveBoundedImageAsBase64(String uri, int maxHeight, int maxWidth)
118    {
119        Request request = ContextHelper.getRequest(_context);
120        
121        ProxiedUri proxiedUri = getProxiedUri(uri);
122        
123        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveBoundedImage(proxiedUri.getType(), proxiedUri.getUri(), maxHeight, maxWidth));
124    }
125
126    @Override
127    public String resolveCroppedImage(String uri, int cropHeight, int cropWidth, boolean download, boolean absolute, boolean internal)
128    {
129        Request request = ContextHelper.getRequest(_context);
130        
131        ProxiedUri proxiedUri = getProxiedUri(uri);
132        
133        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveCroppedImage(proxiedUri.getType(), proxiedUri.getUri(), cropHeight, cropWidth, download, false, false));
134    }
135
136    @Override
137    public String resolveCroppedImageAsBase64(String uri, int cropHeight, int cropWidth)
138    {
139        Request request = ContextHelper.getRequest(_context);
140        
141        ProxiedUri proxiedUri = getProxiedUri(uri);
142        
143        return getAbsoluteRemoteUri(request, ResolveURIComponent.resolveCroppedImage(proxiedUri.getType(), proxiedUri.getUri(), cropHeight, cropWidth));
144    }
145
146    @Override
147    public CHECK checkLink(String uri, boolean shortTest)
148    {
149        ProxiedUri proxiedUri = getProxiedUri(uri);
150
151        URIResolver resolver = _uriResolverEP.getResolverForType(proxiedUri.getType());
152        if (resolver != null)
153        {
154            return resolver.checkLink(proxiedUri.getUri(), shortTest);
155        }
156        
157        return CHECK.NOT_FOUND;
158    }
159
160    @Override
161    public I18nizableText getLabel(String uri)
162    {
163        ProxiedUri proxiedUri = getProxiedUri(uri);
164        URIResolver resolver = _uriResolverEP.getResolverForType(proxiedUri.getType());
165        if (resolver != null)
166        {
167            I18nizableText label = resolver.getLabel(proxiedUri.getUri());
168            Map<String, I18nizableText> params = new HashMap<>();
169            params.put("uri", label);
170            return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_REMOTE_LABEL", params);
171        }
172        
173        return new I18nizableText("plugin.cms", "PLUGINS_CMS_LINK_REMOTE_UNKNOWN_TYPE", Collections.singletonList(proxiedUri.getType()));
174    }
175    
176    /**
177     * Get the absolute proxied uri
178     * @param request the request
179     * @param resolvedUri the resolved uri
180     * @return the absolute proxied url
181     */
182    protected String getAbsoluteRemoteUri(Request request, String resolvedUri)
183    {
184        String uriPrefix = getUriPrefix(request);
185        return getAbsoluteRemoteUriPrefix() + (resolvedUri.startsWith(uriPrefix) ? StringUtils.substringAfter(resolvedUri, uriPrefix) : resolvedUri);
186    }
187    
188    
189    /**
190     * Get the absolute URI prefix
191     * @return the URI prefix
192     */
193    protected String getAbsoluteRemoteUriPrefix()
194    {
195        StringBuilder result = new StringBuilder();
196        
197        String cmsUrl = Config.getInstance().getValueAsString("cms.url");
198        
199        result.append(cmsUrl)
200            .append(cmsUrl.endsWith("/") ? "" : "/")
201            .append("_cms/remote");
202            
203        return result.toString();
204    }
205    
206    /**
207     * Get the URI prefix (non absolute) for resolved uri
208     * @param request The request
209     * @return the URI prefix
210     */
211    protected String getUriPrefix(Request request)
212    {
213        String workspaceURI = (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_URI);
214        return request.getContextPath() + workspaceURI;
215    }
216    
217    /**
218     * Get the proxied uri
219     * @param uri The requested uri
220     * @return the proxied uri or null
221     * @throws IllegalArgumentException The uri does match the expected pattern
222     */
223    protected ProxiedUri getProxiedUri(String uri) throws IllegalArgumentException
224    {
225        Matcher matcher = __URI_PATTERN.matcher(uri);
226        if (matcher.matches())
227        {
228            String type = matcher.group(1);
229            String initialUri = matcher.group(2);
230
231            return new ProxiedUri(type, initialUri);
232        }
233        
234        throw new IllegalArgumentException("The uri " + uri + " does not match the regular expression " + __URI_PATTERN.pattern());
235    }
236    
237    /**
238     * Proxied URI
239     *
240     */
241    protected class ProxiedUri
242    {
243        private String _uri;
244        private String _type;
245        
246        /**
247         * Constructor
248         * @param type The type
249         * @param uri The uri to proxy
250         */
251        public ProxiedUri(String type, String uri)
252        {
253            _type = type;
254            _uri = uri;
255        }
256        
257        /**
258         * Get the uri
259         * @return the uri
260         */
261        public String getUri()
262        {
263            return _uri;
264        }
265        
266        
267        /**
268         * Get the object.
269         * @return the object
270         */
271        public String getType()
272        {
273            return _type;
274        }
275    }
276
277}