001 /*
002 * Apache License
003 * Version 2.0, January 2004
004 * http://www.apache.org/licenses/
005 *
006 * Copyright 2008-2010 by chenillekit.org
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 */
014
015 package org.chenillekit.tapestry.core.components;
016
017 import java.awt.image.BufferedImage;
018 import java.io.ByteArrayInputStream;
019 import java.io.ByteArrayOutputStream;
020 import java.io.IOException;
021 import javax.imageio.ImageIO;
022
023 import org.apache.commons.codec.DecoderException;
024 import org.apache.commons.codec.EncoderException;
025 import org.apache.commons.codec.net.BCodec;
026 import org.apache.tapestry5.BindingConstants;
027 import org.apache.tapestry5.ComponentResources;
028 import org.apache.tapestry5.Link;
029 import org.apache.tapestry5.MarkupWriter;
030 import org.apache.tapestry5.StreamResponse;
031 import org.apache.tapestry5.annotations.OnEvent;
032 import org.apache.tapestry5.annotations.Parameter;
033 import org.apache.tapestry5.annotations.Persist;
034 import org.apache.tapestry5.corelib.base.AbstractField;
035 import org.apache.tapestry5.ioc.annotations.Inject;
036 import org.apache.tapestry5.services.Request;
037
038 import org.chenillekit.image.services.CaptchaProducer;
039 import org.chenillekit.tapestry.core.utils.JPEGInline;
040
041 /**
042 * A Captcha is a type of challenge-response test used in computing to ensure that the response is not generated by a computer.
043 * The process usually involves one computer (a server) asking a user to complete a simple test which the computer is able to
044 * generate and grade. Because other computers are unable to solve the CAPTCHA, any user entering a correct solution is presumed
045 * to be human. Thus, it is sometimes described as a reverse Turing test, because it is administered by a machine and targeted to
046 * a human, in contrast to the standard Turing test that is typically administered by a human and targeted to a machine. A common
047 * type of CAPTCHA requires that the user type letters or digits from a distorted image that appears on the screen.
048 * <p/>
049 * This component based on <a href="http://code.google.com/p/kaptcha/">kaptcha library</a> and produce following HTML code:
050 * <p/>
051 * <pre>
052 * <span id="kaptcha1" class="ck-kaptcha">
053 * <img id="kaptcha1_kaptcha" class="ck-kaptcha" src="..."/>
054 * <input id="kaptcha1_input" class="ck-kaptcha" type="text" name="kaptcha1"/>
055 * </span>
056 * </pre>
057 * <p/>
058 * so you can change the design by cascading style sheets by the "ck-kaptcha" class.
059 * <br/>
060 * To use this component, you need the <a href="http://www.chenillekit.org/chenillekit-image/index.html">chenillekit-image library</a> in you classpath.
061 *
062 * @version $Id: Kaptcha.java 725 2010-11-03 19:40:03Z homburgs $
063 */
064 public class Kaptcha extends AbstractField
065 {
066 private static final String EVENT_NAME = "kaptchaEvent";
067
068 @Parameter(required = true, defaultPrefix = BindingConstants.PROP)
069 private boolean value;
070
071 @Persist
072 private String kaptchaValue;
073
074 private String textFieldValue;
075
076 /**
077 * ComponentResources. For blocks, messages, crete actionlink, trigger event
078 */
079 @Inject
080 private ComponentResources resources;
081
082 /**
083 * Request object for information on current request
084 */
085 @Inject
086 private Request request;
087
088 @Inject
089 private CaptchaProducer kaptchaProducer;
090
091
092 /**
093 * Tapestry render phase method.
094 * Start a tag here, end it in afterRender
095 */
096 void beginRender(MarkupWriter writer)
097 {
098 writer.element("span",
099 "id", getClientId(),
100 "class", "ck-kaptcha");
101
102 writer.element("img",
103 "id", String.format("%s_kaptcha", getClientId()),
104 "src", getImageLink(),
105 "class", "ck-kaptcha");
106 writer.end(); // img
107
108 // <input t:id="textField" type="text" class="ck-kaptcha-input"/>
109
110 writer.element("input",
111 "id", String.format("%s_input", getClientId()),
112 "type", "text",
113 "name", getControlName(),
114 "value", textFieldValue,
115 "class", "ck-kaptcha");
116 writer.end(); // input
117 }
118
119 /**
120 * Tapestry render phase method. End a tag here.
121 */
122 void afterRender(MarkupWriter writer)
123 {
124 writer.end(); // span
125 }
126
127 private Link getImageLink()
128 {
129 BCodec bCodec = new BCodec();
130 try
131 {
132 return resources.createEventLink(EVENT_NAME, bCodec.encode(kaptchaProducer.createText()));
133 }
134 catch (EncoderException e)
135 {
136 throw new RuntimeException(e);
137 }
138 }
139
140 @OnEvent(value = EVENT_NAME)
141 public StreamResponse onKaptchaImage(String kaptchaValue)
142 {
143 BCodec bCodec = new BCodec();
144 try
145 {
146 this.kaptchaValue = bCodec.decode(kaptchaValue);
147 }
148 catch (DecoderException e)
149 {
150 throw new RuntimeException(e);
151 }
152 BufferedImage kapatchImage = kaptchaProducer.createImage(this.kaptchaValue);
153
154 try
155 {
156 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
157 ImageIO.write(kapatchImage, "jpg", byteArrayOutputStream);
158 ByteArrayInputStream bais = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
159 return new JPEGInline(bais, (String[])null);
160 }
161 catch (IOException e)
162 {
163 throw new RuntimeException(e);
164 }
165 }
166
167 /**
168 * Method implemented by subclasses to actually do the work of processing the submission of the form. The element's
169 * elementName property will already have been set. This method is only invoked if the field is <strong>not {@link
170 * #isDisabled() disabled}</strong>.
171 *
172 * @param elementName the name of the element (used to find the correct parameter in the request)
173 */
174 protected void processSubmission(String elementName)
175 {
176 String rawValue = request.getParameter(elementName);
177 value = this.kaptchaValue.equals(rawValue);
178 }
179 }