    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.kaptcha.components;
    014
    015import org.apache.tapestry5.ComponentResources;
    016import org.apache.tapestry5.Link;
    017import org.apache.tapestry5.MarkupWriter;
    018import org.apache.tapestry5.StreamResponse;
    019import org.apache.tapestry5.annotations.Persist;
    020import org.apache.tapestry5.annotations.SupportsInformalParameters;
    021import org.apache.tapestry5.ioc.annotations.Inject;
    022import org.apache.tapestry5.kaptcha.services.KaptchaProducer;
    023import org.apache.tapestry5.services.HttpError;
    024import org.apache.tapestry5.services.Response;
    025
    026import javax.imageio.ImageIO;
    027import javax.servlet.http.HttpServletResponse;
    028import java.awt.image.BufferedImage;
    029import java.io.ByteArrayInputStream;
    030import java.io.ByteArrayOutputStream;
    031import java.io.IOException;
    032import java.io.InputStream;
    033
    034/**
    035 * Part of a Captcha based authentication scheme; a KaptchaImage generates a new
    036 * text image whenever it <em>renders</em> and can provide the previously
    037 * rendered text subsequently (it is stored persistently in the session).
    038 *
    039 * The component renders an {@code <img>} tag, including width and height attributes. Other attributes
    040 * come from informal parameters.
    041 *
    042 * @tapestrydoc
    043 * @since 5.3
    044 */
    045@SupportsInformalParameters
    046public class KaptchaImage
    047{
    048
    049    @Persist
    050    private String captchaText;
    051
    052    @Inject
    053    private KaptchaProducer producer;
    054
    055    @Inject
    056    private ComponentResources resources;
    057
    058    @Inject
    059    private Response response;
    060
    061    public String getCaptchaText()
    062    {
    063        return captchaText;
    064    }
    065
    066    void setupRender()
    067    {
    068        captchaText = producer.createText();
    069    }
    070
    071    boolean beginRender(MarkupWriter writer)
    072    {
    073        Link link = resources.createEventLink("image");
    074
    075        writer.element("img",
    076
    077                "src", link.toURI(),
    078
    079                "width", producer.getWidth(),
    080
    081                "height", producer.getHeight());
    082
    083        resources.renderInformalParameters(writer);
    084
    085        writer.end();
    086
    087        return false;
    088    }
    089
    090    Object onImage() throws IOException
    091    {
    092        if (captchaText == null)
    093        {
    094            return new HttpError(HttpServletResponse.SC_NOT_FOUND, "Session expired.");
    095        }
    096
    097        return new StreamResponse()
    098        {
    099            @Override
    100            public String getContentType()
    101            {
    102                return "image/jpeg";
    103            }
    104
    105            @Override
    106            public InputStream getStream() throws IOException
    107            {
    108                BufferedImage image = producer.createImage(captchaText);
    109
    110                ByteArrayOutputStream baos = new ByteArrayOutputStream();
    111
    112                ImageIO.write(image, "jpg", baos);
    113
    114                return new ByteArrayInputStream(baos.toByteArray());
    115            }
    116
    117            @Override
    118            public void prepareResponse(Response response)
    119            {
    120                response.setDateHeader("Expires", 0);
    121                // Set standard HTTP/1.1 no-cache headers.
    122                response.addHeader("Cache-Control", "no-store, no-cache, must-revalidate");
    123                // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
    124                response.addHeader("Cache-Control", "post-check=0, pre-check=0");
    125                // Set standard HTTP/1.0 no-cache header.
    126                response.setHeader("Pragma", "no-cache");
    127            }
    128        };
    129
    130    }
    131}