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 * <p/>
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}