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.framework.activity.Disposable;
024import org.apache.avalon.framework.activity.Initializable;
025import org.apache.avalon.framework.logger.AbstractLogEnabled;
026import org.apache.avalon.framework.parameters.Parameters;
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.ProcessingException;
031import org.apache.cocoon.environment.ObjectModelHelper;
032import org.apache.cocoon.environment.Request;
033import org.apache.cocoon.environment.SourceResolver;
034import org.apache.cocoon.reading.Reader;
035import org.apache.commons.lang3.StringUtils;
036import org.apache.hc.client5.http.classic.methods.HttpGet;
037import org.apache.hc.client5.http.config.RequestConfig;
038import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
039import org.apache.hc.core5.http.ClassicHttpResponse;
040import org.apache.hc.core5.http.HttpEntity;
041import org.apache.hc.core5.http.ParseException;
042import org.apache.hc.core5.http.io.entity.EntityUtils;
043import org.apache.hc.core5.io.CloseMode;
044import org.xml.sax.SAXException;
045
046import org.ametys.core.util.HttpUtils;
047
048/**
049 * Reader implementation that add header for Captchetat requests
050 */
051public class CaptchetatReader extends AbstractLogEnabled implements Reader, Serviceable, Initializable, Disposable
052{
053    /** The CaptchEtat helper */
054    protected CaptchEtatHelper _captchEtatHelper;
055
056    private OutputStream _os;
057
058    private CloseableHttpClient _httpClient;
059    private ClassicHttpResponse _response;
060
061
062    public void service(ServiceManager manager) throws ServiceException
063    {
064        _captchEtatHelper = (CaptchEtatHelper) manager.lookup(CaptchEtatHelper.ROLE);
065    }
066    
067    public void initialize() throws Exception
068    {
069        _httpClient = HttpUtils.createHttpClient(0, 20, false);
070    }
071    
072    public void dispose()
073    {
074        _httpClient.close(CloseMode.GRACEFUL);
075    }
076    
077    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException
078    {
079
080        Request request = ObjectModelHelper.getRequest(objectModel);
081        
082        String url = _captchEtatHelper.getEndpoint() + "simple-captcha-endpoint?" + StringUtils.defaultString(request.getQueryString());
083        
084        HttpGet httpGet = new HttpGet(url);
085        RequestConfig config = RequestConfig.custom()
086                .setRedirectsEnabled(false)
087                .build();
088        httpGet.setConfig(config);
089        
090        String token = _captchEtatHelper.getToken();
091        httpGet.addHeader("Authorization", "Bearer " + token);
092
093        _response = _httpClient.executeOpen(null, httpGet, null);
094        switch (_response.getCode())
095        {
096            case 200:
097                break;
098            
099            case 403:
100                _response.close();
101                throw new IllegalStateException("CaptchEtat refused the connection");
102                
103            case 500:
104            default:
105                try
106                {
107                    throw new IllegalStateException("CaptchEtat returned an error: " + EntityUtils.toString(_response.getEntity(), StandardCharsets.UTF_8));
108                }
109                catch (ParseException e)
110                {
111                    throw new IllegalStateException("CaptchEtat returned an error");
112                }
113                finally
114                {
115                    _response.close();
116                }
117        }
118    }
119
120    public void setOutputStream(OutputStream out) throws IOException
121    {
122        _os = out;
123    }
124
125    public String getMimeType()
126    {
127        HttpEntity entity = _response.getEntity();
128        return entity != null ? entity.getContentType() : null;
129    }
130
131    public boolean shouldSetContentLength()
132    {
133        return false;
134    }
135
136    public void generate() throws IOException, SAXException, ProcessingException
137    {
138        try
139        {
140            _response.getEntity().writeTo(_os);
141        }
142        finally
143        {
144            _response.close();
145        }
146    }
147
148    public long getLastModified()
149    {
150        return 0;
151    }
152}