001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.captcha.simplecaptcha;
016    
017    import com.liferay.portal.kernel.captcha.Captcha;
018    import com.liferay.portal.kernel.captcha.CaptchaTextException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.ContentTypes;
022    import com.liferay.portal.kernel.util.InstancePool;
023    import com.liferay.portal.kernel.util.ParamUtil;
024    import com.liferay.portal.kernel.util.Randomizer;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.util.PortalUtil;
027    import com.liferay.portal.util.PropsValues;
028    import com.liferay.portal.util.WebKeys;
029    
030    import java.io.IOException;
031    
032    import javax.portlet.PortletRequest;
033    import javax.portlet.PortletResponse;
034    import javax.portlet.PortletSession;
035    
036    import javax.servlet.http.HttpServletRequest;
037    import javax.servlet.http.HttpServletResponse;
038    import javax.servlet.http.HttpSession;
039    
040    import nl.captcha.backgrounds.BackgroundProducer;
041    import nl.captcha.gimpy.GimpyRenderer;
042    import nl.captcha.noise.NoiseProducer;
043    import nl.captcha.servlet.CaptchaServletUtil;
044    import nl.captcha.text.producer.TextProducer;
045    import nl.captcha.text.renderer.WordRenderer;
046    
047    /**
048     * @author Brian Wing Shun Chan
049     */
050    public class SimpleCaptchaImpl implements Captcha {
051    
052            public SimpleCaptchaImpl() {
053                    initBackgroundProducers();
054                    initGimpyRenderers();
055                    initNoiseProducers();
056                    initTextProducers();
057                    initWordRenderers();
058            }
059    
060            public void check(HttpServletRequest request) throws CaptchaTextException {
061                    if (!isEnabled(request)) {
062                            return;
063                    }
064    
065                    HttpSession session = request.getSession();
066    
067                    String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
068    
069                    if (captchaText == null) {
070                            _log.error(
071                                    "Captcha text is null. User " + request.getRemoteUser() +
072                                            " may be trying to circumvent the captcha.");
073    
074                            throw new CaptchaTextException();
075                    }
076    
077                    if (!captchaText.equals(ParamUtil.getString(request, "captchaText"))) {
078                            throw new CaptchaTextException();
079                    }
080    
081                    if (_log.isDebugEnabled()) {
082                            _log.debug("Captcha text is valid");
083                    }
084    
085                    session.removeAttribute(WebKeys.CAPTCHA_TEXT);
086    
087                    if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
088                            (Validator.isNotNull(request.getRemoteUser()))) {
089    
090                            Integer count = (Integer)session.getAttribute(
091                                    WebKeys.CAPTCHA_COUNT);
092    
093                            if (count == null) {
094                                    count = new Integer(1);
095                            }
096                            else {
097                                    count = new Integer(count.intValue() + 1);
098                            }
099    
100                            session.setAttribute(WebKeys.CAPTCHA_COUNT, count);
101                    }
102            }
103    
104            public void check(PortletRequest portletRequest)
105                    throws CaptchaTextException {
106    
107                    if (!isEnabled(portletRequest)) {
108                            return;
109                    }
110    
111                    PortletSession portletSession = portletRequest.getPortletSession();
112    
113                    String captchaText = (String)portletSession.getAttribute(
114                            WebKeys.CAPTCHA_TEXT);
115    
116                    if (captchaText == null) {
117                            _log.error(
118                                    "Captcha text is null. User " + portletRequest.getRemoteUser() +
119                                            " may be trying to circumvent the captcha.");
120    
121                            throw new CaptchaTextException();
122                    }
123    
124                    if (!captchaText.equals(
125                                    ParamUtil.getString(portletRequest, "captchaText"))) {
126    
127                            throw new CaptchaTextException();
128                    }
129    
130                    if (_log.isDebugEnabled()) {
131                            _log.debug("Captcha text is valid");
132                    }
133    
134                    portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
135    
136                    if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
137                            (Validator.isNotNull(portletRequest.getRemoteUser()))) {
138    
139                            Integer count = (Integer)portletSession.getAttribute(
140                                    WebKeys.CAPTCHA_COUNT);
141    
142                            if (count == null) {
143                                    count = new Integer(1);
144                            }
145                            else {
146                                    count = new Integer(count.intValue() + 1);
147                            }
148    
149                            portletSession.setAttribute(WebKeys.CAPTCHA_COUNT, count);
150                    }
151            }
152    
153            public String getTaglibPath() {
154                    return _TAGLIB_PATH;
155            }
156    
157            public boolean isEnabled(HttpServletRequest request) {
158                    if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
159                            HttpSession session = request.getSession();
160    
161                            Integer count = (Integer)session.getAttribute(
162                                    WebKeys.CAPTCHA_COUNT);
163    
164                            if ((count != null) &&
165                                    (PropsValues.CAPTCHA_MAX_CHALLENGES <= count.intValue())) {
166    
167                                    return false;
168                            }
169                            else {
170                                    return true;
171                            }
172                    }
173                    else if (PropsValues.CAPTCHA_MAX_CHALLENGES < 0) {
174                            return false;
175                    }
176                    else {
177                            return true;
178                    }
179            }
180    
181            public boolean isEnabled(PortletRequest portletRequest) {
182                    if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
183                            PortletSession portletSession = portletRequest.getPortletSession();
184    
185                            Integer count = (Integer)portletSession.getAttribute(
186                                    WebKeys.CAPTCHA_COUNT);
187    
188                            if ((count != null) &&
189                                    (PropsValues.CAPTCHA_MAX_CHALLENGES <= count.intValue())) {
190    
191                                    return false;
192                            }
193                            else {
194                                    return true;
195                            }
196                    }
197                    else if (PropsValues.CAPTCHA_MAX_CHALLENGES < 0) {
198                            return false;
199                    }
200                    else {
201                            return true;
202                    }
203            }
204    
205            public void serveImage(
206                            HttpServletRequest request, HttpServletResponse response)
207                    throws IOException {
208    
209                    HttpSession session = request.getSession();
210    
211                    nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
212    
213                    session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
214    
215                    response.setContentType(ContentTypes.IMAGE_JPEG);
216    
217                    CaptchaServletUtil.writeImage(
218                            response.getOutputStream(), simpleCaptcha.getImage());
219            }
220    
221            public void serveImage(
222                            PortletRequest portletRequest, PortletResponse portletResponse)
223                    throws IOException {
224    
225                    PortletSession portletSession = portletRequest.getPortletSession();
226    
227                    nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
228    
229                    portletSession.setAttribute(
230                            WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
231    
232                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
233                            portletResponse);
234    
235                    CaptchaServletUtil.writeImage(
236                            response.getOutputStream(), simpleCaptcha.getImage());
237            }
238    
239            protected BackgroundProducer getBackgroundProducer() {
240                    if (_backgroundProducers.length == 1) {
241                            return _backgroundProducers[0];
242                    }
243    
244                    Randomizer randomizer = Randomizer.getInstance();
245    
246                    int pos = randomizer.nextInt(_backgroundProducers.length);
247    
248                    return _backgroundProducers[pos];
249            }
250    
251            protected GimpyRenderer getGimpyRenderer() {
252                    if (_gimpyRenderers.length == 1) {
253                            return _gimpyRenderers[0];
254                    }
255    
256                    Randomizer randomizer = Randomizer.getInstance();
257    
258                    int pos = randomizer.nextInt(_gimpyRenderers.length);
259    
260                    return _gimpyRenderers[pos];
261            }
262    
263            protected int getHeight() {
264                    return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
265            }
266    
267            protected NoiseProducer getNoiseProducer() {
268                    if (_noiseProducers.length == 1) {
269                            return _noiseProducers[0];
270                    }
271    
272                    Randomizer randomizer = Randomizer.getInstance();
273    
274                    int pos = randomizer.nextInt(_noiseProducers.length);
275    
276                    return _noiseProducers[pos];
277            }
278    
279            protected nl.captcha.Captcha getSimpleCaptcha() {
280                    nl.captcha.Captcha.Builder captchaBuilder =
281                            new nl.captcha.Captcha.Builder(getWidth(), getHeight());
282    
283                    captchaBuilder.addText(getTextProducer(), getWordRenderer());
284                    captchaBuilder.addBackground(getBackgroundProducer());
285                    captchaBuilder.gimp(getGimpyRenderer());
286                    captchaBuilder.addNoise(getNoiseProducer());
287                    captchaBuilder.addBorder();
288    
289                    return captchaBuilder.build();
290            }
291    
292            protected TextProducer getTextProducer() {
293                    if (_textProducers.length == 1) {
294                            return _textProducers[0];
295                    }
296    
297                    Randomizer randomizer = Randomizer.getInstance();
298    
299                    int pos = randomizer.nextInt(_textProducers.length);
300    
301                    return _textProducers[pos];
302            }
303    
304            protected int getWidth() {
305                    return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
306            }
307    
308            protected WordRenderer getWordRenderer() {
309                    if (_wordRenderers.length == 1) {
310                            return _wordRenderers[0];
311                    }
312    
313                    Randomizer randomizer = Randomizer.getInstance();
314    
315                    int pos = randomizer.nextInt(_wordRenderers.length);
316    
317                    return _wordRenderers[pos];
318            }
319    
320            protected void initBackgroundProducers() {
321                    String[] backgroundProducerClassNames =
322                            PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
323    
324                    _backgroundProducers = new BackgroundProducer[
325                            backgroundProducerClassNames.length];
326    
327                    for (int i = 0; i < backgroundProducerClassNames.length; i++) {
328                            String backgroundProducerClassName =
329                                    backgroundProducerClassNames[i];
330    
331                            _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
332                                    backgroundProducerClassName);
333                    }
334            }
335    
336            protected void initGimpyRenderers() {
337                    String[] gimpyRendererClassNames =
338                            PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
339    
340                    _gimpyRenderers = new GimpyRenderer[
341                            gimpyRendererClassNames.length];
342    
343                    for (int i = 0; i < gimpyRendererClassNames.length; i++) {
344                            String gimpyRendererClassName =
345                                    gimpyRendererClassNames[i];
346    
347                            _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
348                                    gimpyRendererClassName);
349                    }
350            }
351    
352            protected void initNoiseProducers() {
353                    String[] noiseProducerClassNames =
354                            PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
355    
356                    _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
357    
358                    for (int i = 0; i < noiseProducerClassNames.length; i++) {
359                            String noiseProducerClassName = noiseProducerClassNames[i];
360    
361                            _noiseProducers[i] = (NoiseProducer)InstancePool.get(
362                                    noiseProducerClassName);
363                    }
364            }
365    
366            protected void initTextProducers() {
367                    String[] textProducerClassNames =
368                            PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
369    
370                    _textProducers = new TextProducer[textProducerClassNames.length];
371    
372                    for (int i = 0; i < textProducerClassNames.length; i++) {
373                            String textProducerClassName = textProducerClassNames[i];
374    
375                            _textProducers[i] = (TextProducer)InstancePool.get(
376                                    textProducerClassName);
377                    }
378            }
379    
380            protected void initWordRenderers() {
381                    String[] wordRendererClassNames =
382                            PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
383    
384                    _wordRenderers = new WordRenderer[wordRendererClassNames.length];
385    
386                    for (int i = 0; i < wordRendererClassNames.length; i++) {
387                            String wordRendererClassName = wordRendererClassNames[i];
388    
389                            _wordRenderers[i] = (WordRenderer)InstancePool.get(
390                                    wordRendererClassName);
391                    }
392            }
393    
394            private static final String _TAGLIB_PATH =
395                    "/html/taglib/ui/captcha/simplecaptcha.jsp";
396    
397            private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
398    
399            private BackgroundProducer[] _backgroundProducers;
400            private GimpyRenderer[] _gimpyRenderers;
401            private NoiseProducer[] _noiseProducers;
402            private TextProducer[] _textProducers;
403            private WordRenderer[] _wordRenderers;
404    
405    }