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