1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.captcha.simplecaptcha;
16  
17  import com.liferay.portal.kernel.captcha.Captcha;
18  import com.liferay.portal.kernel.captcha.CaptchaTextException;
19  import com.liferay.portal.kernel.log.Log;
20  import com.liferay.portal.kernel.log.LogFactoryUtil;
21  import com.liferay.portal.kernel.util.ContentTypes;
22  import com.liferay.portal.kernel.util.InstancePool;
23  import com.liferay.portal.kernel.util.ParamUtil;
24  import com.liferay.portal.kernel.util.Randomizer;
25  import com.liferay.portal.kernel.util.Validator;
26  import com.liferay.portal.util.PortalUtil;
27  import com.liferay.portal.util.PropsValues;
28  import com.liferay.portal.util.WebKeys;
29  
30  import java.io.IOException;
31  
32  import javax.portlet.PortletRequest;
33  import javax.portlet.PortletResponse;
34  import javax.portlet.PortletSession;
35  
36  import javax.servlet.http.HttpServletRequest;
37  import javax.servlet.http.HttpServletResponse;
38  import javax.servlet.http.HttpSession;
39  
40  import nl.captcha.backgrounds.BackgroundProducer;
41  import nl.captcha.gimpy.GimpyRenderer;
42  import nl.captcha.noise.NoiseProducer;
43  import nl.captcha.servlet.CaptchaServletUtil;
44  import nl.captcha.text.producer.TextProducer;
45  import nl.captcha.text.renderer.WordRenderer;
46  
47  /**
48   * <a href="SimpleCaptchaImpl.java.html"><b><i>View Source</i></b></a>
49   *
50   * @author Brian Wing Shun Chan
51   */
52  public class SimpleCaptchaImpl implements Captcha {
53  
54      public SimpleCaptchaImpl() {
55          initBackgroundProducers();
56          initGimpyRenderers();
57          initNoiseProducers();
58          initTextProducers();
59          initWordRenderers();
60      }
61  
62      public void check(HttpServletRequest request) throws CaptchaTextException {
63          if (!isEnabled(request)) {
64              return;
65          }
66  
67          HttpSession session = request.getSession();
68  
69          String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
70  
71          if (captchaText == null) {
72              _log.error(
73                  "Captcha text is null. User " + request.getRemoteUser() +
74                      " may be trying to circumvent the captcha.");
75  
76              throw new CaptchaTextException();
77          }
78  
79          if (!captchaText.equals(ParamUtil.getString(request, "captchaText"))) {
80              throw new CaptchaTextException();
81          }
82  
83          if (_log.isDebugEnabled()) {
84              _log.debug("Captcha text is valid");
85          }
86  
87          session.removeAttribute(WebKeys.CAPTCHA_TEXT);
88  
89          if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
90              (Validator.isNotNull(request.getRemoteUser()))) {
91  
92              Integer count = (Integer)session.getAttribute(
93                  WebKeys.CAPTCHA_COUNT);
94  
95              if (count == null) {
96                  count = new Integer(1);
97              }
98              else {
99                  count = new Integer(count.intValue() + 1);
100             }
101 
102             session.setAttribute(WebKeys.CAPTCHA_COUNT, count);
103         }
104     }
105 
106     public void check(PortletRequest portletRequest)
107         throws CaptchaTextException {
108 
109         if (!isEnabled(portletRequest)) {
110             return;
111         }
112 
113         PortletSession portletSession = portletRequest.getPortletSession();
114 
115         String captchaText = (String)portletSession.getAttribute(
116             WebKeys.CAPTCHA_TEXT);
117 
118         if (captchaText == null) {
119             _log.error(
120                 "Captcha text is null. User " + portletRequest.getRemoteUser() +
121                     " may be trying to circumvent the captcha.");
122 
123             throw new CaptchaTextException();
124         }
125 
126         if (!captchaText.equals(
127                 ParamUtil.getString(portletRequest, "captchaText"))) {
128 
129             throw new CaptchaTextException();
130         }
131 
132         if (_log.isDebugEnabled()) {
133             _log.debug("Captcha text is valid");
134         }
135 
136         portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
137 
138         if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
139             (Validator.isNotNull(portletRequest.getRemoteUser()))) {
140 
141             Integer count = (Integer)portletSession.getAttribute(
142                 WebKeys.CAPTCHA_COUNT);
143 
144             if (count == null) {
145                 count = new Integer(1);
146             }
147             else {
148                 count = new Integer(count.intValue() + 1);
149             }
150 
151             portletSession.setAttribute(WebKeys.CAPTCHA_COUNT, count);
152         }
153     }
154 
155     public String getTaglibPath() {
156         return _TAGLIB_PATH;
157     }
158 
159     public boolean isEnabled(HttpServletRequest request) {
160         if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
161             HttpSession session = request.getSession();
162 
163             Integer count = (Integer)session.getAttribute(
164                 WebKeys.CAPTCHA_COUNT);
165 
166             if ((count != null) &&
167                 (PropsValues.CAPTCHA_MAX_CHALLENGES <= count.intValue())) {
168 
169                 return false;
170             }
171             else {
172                 return true;
173             }
174         }
175         else if (PropsValues.CAPTCHA_MAX_CHALLENGES < 0) {
176             return false;
177         }
178         else {
179             return true;
180         }
181     }
182 
183     public boolean isEnabled(PortletRequest portletRequest) {
184         if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
185             PortletSession portletSession = portletRequest.getPortletSession();
186 
187             Integer count = (Integer)portletSession.getAttribute(
188                 WebKeys.CAPTCHA_COUNT);
189 
190             if ((count != null) &&
191                 (PropsValues.CAPTCHA_MAX_CHALLENGES <= count.intValue())) {
192 
193                 return false;
194             }
195             else {
196                 return true;
197             }
198         }
199         else if (PropsValues.CAPTCHA_MAX_CHALLENGES < 0) {
200             return false;
201         }
202         else {
203             return true;
204         }
205     }
206 
207     public void serveImage(
208             HttpServletRequest request, HttpServletResponse response)
209         throws IOException {
210 
211         HttpSession session = request.getSession();
212 
213         nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
214 
215         session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
216 
217         response.setContentType(ContentTypes.IMAGE_JPEG);
218 
219         CaptchaServletUtil.writeImage(
220             response.getOutputStream(), simpleCaptcha.getImage());
221     }
222 
223     public void serveImage(
224             PortletRequest portletRequest, PortletResponse portletResponse)
225         throws IOException {
226 
227         PortletSession portletSession = portletRequest.getPortletSession();
228 
229         nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
230 
231         portletSession.setAttribute(
232             WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
233 
234         HttpServletResponse response = PortalUtil.getHttpServletResponse(
235             portletResponse);
236 
237         CaptchaServletUtil.writeImage(
238             response.getOutputStream(), simpleCaptcha.getImage());
239     }
240 
241     protected BackgroundProducer getBackgroundProducer() {
242         if (_backgroundProducers.length == 1) {
243             return _backgroundProducers[0];
244         }
245 
246         Randomizer randomizer = Randomizer.getInstance();
247 
248         int pos = randomizer.nextInt(_backgroundProducers.length);
249 
250         return _backgroundProducers[pos];
251     }
252 
253     protected GimpyRenderer getGimpyRenderer() {
254         if (_gimpyRenderers.length == 1) {
255             return _gimpyRenderers[0];
256         }
257 
258         Randomizer randomizer = Randomizer.getInstance();
259 
260         int pos = randomizer.nextInt(_gimpyRenderers.length);
261 
262         return _gimpyRenderers[pos];
263     }
264 
265     protected int getHeight() {
266         return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
267     }
268 
269     protected NoiseProducer getNoiseProducer() {
270         if (_noiseProducers.length == 1) {
271             return _noiseProducers[0];
272         }
273 
274         Randomizer randomizer = Randomizer.getInstance();
275 
276         int pos = randomizer.nextInt(_noiseProducers.length);
277 
278         return _noiseProducers[pos];
279     }
280 
281     protected nl.captcha.Captcha getSimpleCaptcha() {
282         nl.captcha.Captcha.Builder captchaBuilder =
283             new nl.captcha.Captcha.Builder(getWidth(), getHeight());
284 
285         captchaBuilder.addText(getTextProducer(), getWordRenderer());
286         captchaBuilder.addBackground(getBackgroundProducer());
287         captchaBuilder.gimp(getGimpyRenderer());
288         captchaBuilder.addNoise(getNoiseProducer());
289         captchaBuilder.addBorder();
290 
291         return captchaBuilder.build();
292     }
293 
294     protected TextProducer getTextProducer() {
295         if (_textProducers.length == 1) {
296             return _textProducers[0];
297         }
298 
299         Randomizer randomizer = Randomizer.getInstance();
300 
301         int pos = randomizer.nextInt(_textProducers.length);
302 
303         return _textProducers[pos];
304     }
305 
306     protected int getWidth() {
307         return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
308     }
309 
310     protected WordRenderer getWordRenderer() {
311         if (_wordRenderers.length == 1) {
312             return _wordRenderers[0];
313         }
314 
315         Randomizer randomizer = Randomizer.getInstance();
316 
317         int pos = randomizer.nextInt(_wordRenderers.length);
318 
319         return _wordRenderers[pos];
320     }
321 
322     protected void initBackgroundProducers() {
323         String[] backgroundProducerClassNames =
324             PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
325 
326         _backgroundProducers = new BackgroundProducer[
327             backgroundProducerClassNames.length];
328 
329         for (int i = 0; i < backgroundProducerClassNames.length; i++) {
330             String backgroundProducerClassName =
331                 backgroundProducerClassNames[i];
332 
333             _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
334                 backgroundProducerClassName);
335         }
336     }
337 
338     protected void initGimpyRenderers() {
339         String[] gimpyRendererClassNames =
340             PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
341 
342         _gimpyRenderers = new GimpyRenderer[
343             gimpyRendererClassNames.length];
344 
345         for (int i = 0; i < gimpyRendererClassNames.length; i++) {
346             String gimpyRendererClassName =
347                 gimpyRendererClassNames[i];
348 
349             _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
350                 gimpyRendererClassName);
351         }
352     }
353 
354     protected void initNoiseProducers() {
355         String[] noiseProducerClassNames =
356             PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
357 
358         _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
359 
360         for (int i = 0; i < noiseProducerClassNames.length; i++) {
361             String noiseProducerClassName = noiseProducerClassNames[i];
362 
363             _noiseProducers[i] = (NoiseProducer)InstancePool.get(
364                 noiseProducerClassName);
365         }
366     }
367 
368     protected void initTextProducers() {
369         String[] textProducerClassNames =
370             PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
371 
372         _textProducers = new TextProducer[textProducerClassNames.length];
373 
374         for (int i = 0; i < textProducerClassNames.length; i++) {
375             String textProducerClassName = textProducerClassNames[i];
376 
377             _textProducers[i] = (TextProducer)InstancePool.get(
378                 textProducerClassName);
379         }
380     }
381 
382     protected void initWordRenderers() {
383         String[] wordRendererClassNames =
384             PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
385 
386         _wordRenderers = new WordRenderer[wordRendererClassNames.length];
387 
388         for (int i = 0; i < wordRendererClassNames.length; i++) {
389             String wordRendererClassName = wordRendererClassNames[i];
390 
391             _wordRenderers[i] = (WordRenderer)InstancePool.get(
392                 wordRendererClassName);
393         }
394     }
395 
396     private static final String _TAGLIB_PATH =
397         "/html/taglib/ui/captcha/simplecaptcha.jsp";
398 
399     private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
400 
401     private BackgroundProducer[] _backgroundProducers;
402     private GimpyRenderer[] _gimpyRenderers;
403     private NoiseProducer[] _noiseProducers;
404     private TextProducer[] _textProducers;
405     private WordRenderer[] _wordRenderers;
406 
407 }