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    /** The CaptchEtat helper */
049    protected CaptchEtatHelper _captchEtatHelper;
050
051    private OutputStream _os;
052
053    private CloseableHttpClient _httpClient;
054    private CloseableHttpResponse _response;
055
056
057    public void service(ServiceManager manager) throws ServiceException
058    {
059        _captchEtatHelper = (CaptchEtatHelper) manager.lookup(CaptchEtatHelper.ROLE);
060    }
061    
062    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException
063    {
064        RequestConfig config = RequestConfig.custom()
065                .setConnectTimeout(20000)
066                .setConnectionRequestTimeout(20000)
067                .setSocketTimeout(20000).build();
068        
069        _httpClient = HttpClientBuilder.create()
070            .setDefaultRequestConfig(config)
071            .disableRedirectHandling()
072            .useSystemProperties()
073            .build();
074
075        Request request = ObjectModelHelper.getRequest(objectModel);
076        
077        String url = _captchEtatHelper.getEndpoint() + "simple-captcha-endpoint?" + StringUtils.defaultString(request.getQueryString());
078        
079        HttpGet httpGet = new HttpGet(url);
080        String token = _captchEtatHelper.getToken();
081        httpGet.addHeader("Authorization", "Bearer " + token);
082
083        _response = _httpClient.execute(httpGet);
084        switch (_response.getStatusLine().getStatusCode())
085        {
086            case 200:
087                break;
088            
089            case 403:
090                throw new IllegalStateException("CaptchEtat refused the connection");
091                
092            case 500:
093            default:
094                throw new IllegalStateException("CaptchEtat returned an error: " + org.apache.commons.io.IOUtils.toString(_response.getEntity().getContent(), StandardCharsets.UTF_8));
095        }
096    }
097
098    public void setOutputStream(OutputStream out) throws IOException
099    {
100        _os = out;
101    }
102
103    public String getMimeType()
104    {
105        Header contentType = _response.getEntity().getContentType();
106        return contentType != null ? contentType.getValue() : null;
107    }
108
109    public boolean shouldSetContentLength()
110    {
111        return false;
112    }
113
114    public void generate() throws IOException, SAXException, ProcessingException
115    {
116        _response.getEntity().writeTo(_os);
117    }
118
119    public long getLastModified()
120    {
121        return 0;
122    }
123
124    public void recycle()
125    {
126        try
127        {
128            _response.close();
129        }
130        catch (IOException e)
131        {
132            getLogger().error("Can't close response", e);
133        }
134    }
135}