001/*
002 *  Copyright 2023 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.captchetat.captcha;
017
018import java.io.IOException;
019import java.io.OutputStream;
020import java.nio.charset.StandardCharsets;
021import java.util.Map;
022
023import org.apache.avalon.excalibur.pool.Recyclable;
024import org.apache.avalon.framework.logger.AbstractLogEnabled;
025import org.apache.avalon.framework.parameters.Parameters;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.environment.ObjectModelHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.cocoon.environment.SourceResolver;
033import org.apache.cocoon.reading.Reader;
034import org.apache.commons.lang3.StringUtils;
035import org.apache.http.Header;
036import org.apache.http.client.config.RequestConfig;
037import org.apache.http.client.methods.CloseableHttpResponse;
038import org.apache.http.client.methods.HttpGet;
039import org.apache.http.impl.client.CloseableHttpClient;
040import org.apache.http.impl.client.HttpClientBuilder;
041import org.xml.sax.SAXException;
042
043/**
044 * Reader implementation that add header for Captchetat requests
045 */
046public class CaptchetatReader extends AbstractLogEnabled implements Reader, Recyclable, Serviceable
047{    
048
049    private OutputStream _os;
050
051    private CloseableHttpClient _httpClient;
052    private CloseableHttpResponse _response;
053
054    private CaptchEtatHelper _captchEtatHelper;
055
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        _captchEtatHelper = (CaptchEtatHelper) manager.lookup(CaptchEtatHelper.ROLE);
059    }
060    
061    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException
062    {
063        RequestConfig config = RequestConfig.custom()
064                .setConnectTimeout(20000)
065                .setConnectionRequestTimeout(20000)
066                .setSocketTimeout(20000).build();
067        
068        _httpClient = HttpClientBuilder.create()
069            .setDefaultRequestConfig(config)
070            .disableRedirectHandling()
071            .useSystemProperties()
072            .build();
073
074        Request request = ObjectModelHelper.getRequest(objectModel);
075        
076        String url = _captchEtatHelper.getEndpoint() + "simple-captcha-endpoint?" + StringUtils.defaultString(request.getQueryString());
077        
078        HttpGet httpGet = new HttpGet(url);
079        String token = _captchEtatHelper.getToken();
080        httpGet.addHeader("Authorization", "Bearer " + token);   
081
082        _response = _httpClient.execute(httpGet);
083        switch (_response.getStatusLine().getStatusCode())
084        {
085            case 200: 
086                break;
087            
088            case 403: 
089                throw new IllegalStateException("CaptchEtat refused the connection");
090                
091            case 500: 
092            default:
093                throw new IllegalStateException("CaptchEtat returned an error: " + org.apache.commons.io.IOUtils.toString(_response.getEntity().getContent(), StandardCharsets.UTF_8));
094        }
095    }
096
097    public void setOutputStream(OutputStream out) throws IOException
098    {
099        _os = out;
100    }
101
102    public String getMimeType()
103    {
104        Header contentType = _response.getEntity().getContentType();
105        return contentType != null ? contentType.getValue() : null;
106    }
107
108    public boolean shouldSetContentLength()
109    {
110        return false;
111    }
112
113    public void generate() throws IOException, SAXException, ProcessingException
114    {
115        _response.getEntity().writeTo(_os);
116    }
117
118    public long getLastModified()
119    {
120        return 0;
121    }
122
123    public void recycle()
124    {
125        try
126        {
127            _response.close();
128        }
129        catch (IOException e)
130        {
131            getLogger().error("Can't close response", e);
132        }
133    }
134}