001
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
053 public class SimpleCaptchaImpl implements Captcha {
054
055 public SimpleCaptchaImpl() {
056 initBackgroundProducers();
057 initGimpyRenderers();
058 initNoiseProducers();
059 initTextProducers();
060 initWordRenderers();
061 }
062
063 @Override
064 public void check(HttpServletRequest request) throws CaptchaException {
065 if (!isEnabled(request)) {
066 return;
067 }
068
069 if (!validateChallenge(request)) {
070 incrementCounter(request);
071
072 checkMaxChallenges(request);
073
074 throw new CaptchaTextException();
075 }
076
077 if (_log.isDebugEnabled()) {
078 _log.debug("CAPTCHA text is valid");
079 }
080 }
081
082 @Override
083 public void check(PortletRequest portletRequest) throws CaptchaException {
084 if (!isEnabled(portletRequest)) {
085 return;
086 }
087
088 if (!validateChallenge(portletRequest)) {
089 incrementCounter(portletRequest);
090
091 checkMaxChallenges(portletRequest);
092
093 throw new CaptchaTextException();
094 }
095
096 if (_log.isDebugEnabled()) {
097 _log.debug("CAPTCHA text is valid");
098 }
099 }
100
101 @Override
102 public String getTaglibPath() {
103 return _TAGLIB_PATH;
104 }
105
106 @Override
107 public boolean isEnabled(HttpServletRequest request)
108 throws CaptchaException {
109
110 checkMaxChallenges(request);
111
112 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
113 return true;
114 }
115 else {
116 return false;
117 }
118 }
119
120 @Override
121 public boolean isEnabled(PortletRequest portletRequest)
122 throws CaptchaException {
123
124 checkMaxChallenges(portletRequest);
125
126 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
127 return true;
128 }
129 else {
130 return false;
131 }
132 }
133
134 @Override
135 public void serveImage(
136 HttpServletRequest request, HttpServletResponse response)
137 throws IOException {
138
139 HttpSession session = request.getSession();
140
141 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
142
143 session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
144
145 response.setContentType(ContentTypes.IMAGE_JPEG);
146
147 CaptchaServletUtil.writeImage(
148 response.getOutputStream(), simpleCaptcha.getImage());
149 }
150
151 @Override
152 public void serveImage(
153 PortletRequest portletRequest, PortletResponse portletResponse)
154 throws IOException {
155
156 PortletSession portletSession = portletRequest.getPortletSession();
157
158 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
159
160 portletSession.setAttribute(
161 WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
162
163 HttpServletResponse response = PortalUtil.getHttpServletResponse(
164 portletResponse);
165
166 response.setContentType(ContentTypes.IMAGE_PNG);
167
168 CaptchaServletUtil.writeImage(
169 response.getOutputStream(), simpleCaptcha.getImage());
170 }
171
172 protected void checkMaxChallenges(HttpServletRequest request)
173 throws CaptchaMaxChallengesException {
174
175 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
176 HttpSession session = request.getSession();
177
178 Integer count = (Integer)session.getAttribute(
179 WebKeys.CAPTCHA_COUNT);
180
181 checkMaxChallenges(count);
182 }
183 }
184
185 protected void checkMaxChallenges(Integer count)
186 throws CaptchaMaxChallengesException {
187
188 if ((count != null) && (count > PropsValues.CAPTCHA_MAX_CHALLENGES)) {
189 throw new CaptchaMaxChallengesException();
190 }
191 }
192
193 protected void checkMaxChallenges(PortletRequest portletRequest)
194 throws CaptchaMaxChallengesException {
195
196 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
197 PortletSession portletSession = portletRequest.getPortletSession();
198
199 Integer count = (Integer)portletSession.getAttribute(
200 WebKeys.CAPTCHA_COUNT);
201
202 checkMaxChallenges(count);
203 }
204 }
205
206 protected BackgroundProducer getBackgroundProducer() {
207 if (_backgroundProducers.length == 1) {
208 return _backgroundProducers[0];
209 }
210
211 Randomizer randomizer = Randomizer.getInstance();
212
213 int pos = randomizer.nextInt(_backgroundProducers.length);
214
215 return _backgroundProducers[pos];
216 }
217
218 protected GimpyRenderer getGimpyRenderer() {
219 if (_gimpyRenderers.length == 1) {
220 return _gimpyRenderers[0];
221 }
222
223 Randomizer randomizer = Randomizer.getInstance();
224
225 int pos = randomizer.nextInt(_gimpyRenderers.length);
226
227 return _gimpyRenderers[pos];
228 }
229
230 protected int getHeight() {
231 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
232 }
233
234 protected NoiseProducer getNoiseProducer() {
235 if (_noiseProducers.length == 1) {
236 return _noiseProducers[0];
237 }
238
239 Randomizer randomizer = Randomizer.getInstance();
240
241 int pos = randomizer.nextInt(_noiseProducers.length);
242
243 return _noiseProducers[pos];
244 }
245
246 protected nl.captcha.Captcha getSimpleCaptcha() {
247 nl.captcha.Captcha.Builder captchaBuilder =
248 new nl.captcha.Captcha.Builder(getWidth(), getHeight());
249
250 captchaBuilder.addText(getTextProducer(), getWordRenderer());
251 captchaBuilder.addBackground(getBackgroundProducer());
252 captchaBuilder.gimp(getGimpyRenderer());
253 captchaBuilder.addNoise(getNoiseProducer());
254 captchaBuilder.addBorder();
255
256 return captchaBuilder.build();
257 }
258
259 protected TextProducer getTextProducer() {
260 if (_textProducers.length == 1) {
261 return _textProducers[0];
262 }
263
264 Randomizer randomizer = Randomizer.getInstance();
265
266 int pos = randomizer.nextInt(_textProducers.length);
267
268 return _textProducers[pos];
269 }
270
271 protected int getWidth() {
272 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
273 }
274
275 protected WordRenderer getWordRenderer() {
276 if (_wordRenderers.length == 1) {
277 return _wordRenderers[0];
278 }
279
280 Randomizer randomizer = Randomizer.getInstance();
281
282 int pos = randomizer.nextInt(_wordRenderers.length);
283
284 return _wordRenderers[pos];
285 }
286
287 protected void incrementCounter(HttpServletRequest request) {
288 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
289 Validator.isNotNull(request.getRemoteUser())) {
290
291 HttpSession session = request.getSession();
292
293 Integer count = (Integer)session.getAttribute(
294 WebKeys.CAPTCHA_COUNT);
295
296 session.setAttribute(
297 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
298 }
299 }
300
301 protected Integer incrementCounter(Integer count) {
302 if (count == null) {
303 count = new Integer(1);
304 }
305 else {
306 count = new Integer(count.intValue() + 1);
307 }
308
309 return count;
310 }
311
312 protected void incrementCounter(PortletRequest portletRequest) {
313 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
314 Validator.isNotNull(portletRequest.getRemoteUser())) {
315
316 PortletSession portletSession = portletRequest.getPortletSession();
317
318 Integer count = (Integer)portletSession.getAttribute(
319 WebKeys.CAPTCHA_COUNT);
320
321 portletSession.setAttribute(
322 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
323 }
324 }
325
326 protected void initBackgroundProducers() {
327 String[] backgroundProducerClassNames =
328 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
329
330 _backgroundProducers = new BackgroundProducer[
331 backgroundProducerClassNames.length];
332
333 for (int i = 0; i < backgroundProducerClassNames.length; i++) {
334 String backgroundProducerClassName =
335 backgroundProducerClassNames[i];
336
337 _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
338 backgroundProducerClassName);
339 }
340 }
341
342 protected void initGimpyRenderers() {
343 String[] gimpyRendererClassNames =
344 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
345
346 _gimpyRenderers = new GimpyRenderer[
347 gimpyRendererClassNames.length];
348
349 for (int i = 0; i < gimpyRendererClassNames.length; i++) {
350 String gimpyRendererClassName = gimpyRendererClassNames[i];
351
352 _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
353 gimpyRendererClassName);
354 }
355 }
356
357 protected void initNoiseProducers() {
358 String[] noiseProducerClassNames =
359 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
360
361 _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
362
363 for (int i = 0; i < noiseProducerClassNames.length; i++) {
364 String noiseProducerClassName = noiseProducerClassNames[i];
365
366 _noiseProducers[i] = (NoiseProducer)InstancePool.get(
367 noiseProducerClassName);
368 }
369 }
370
371 protected void initTextProducers() {
372 String[] textProducerClassNames =
373 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
374
375 _textProducers = new TextProducer[textProducerClassNames.length];
376
377 for (int i = 0; i < textProducerClassNames.length; i++) {
378 String textProducerClassName = textProducerClassNames[i];
379
380 _textProducers[i] = (TextProducer)InstancePool.get(
381 textProducerClassName);
382 }
383 }
384
385 protected void initWordRenderers() {
386 String[] wordRendererClassNames =
387 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
388
389 _wordRenderers = new WordRenderer[wordRendererClassNames.length];
390
391 for (int i = 0; i < wordRendererClassNames.length; i++) {
392 String wordRendererClassName = wordRendererClassNames[i];
393
394 _wordRenderers[i] = (WordRenderer)InstancePool.get(
395 wordRendererClassName);
396 }
397 }
398
399 protected boolean validateChallenge(HttpServletRequest request)
400 throws CaptchaException {
401
402 HttpSession session = request.getSession();
403
404 String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
405
406 if (captchaText == null) {
407 _log.error(
408 "CAPTCHA text is null. User " + request.getRemoteUser() +
409 " may be trying to circumvent the CAPTCHA.");
410
411 throw new CaptchaTextException();
412 }
413
414 boolean valid = captchaText.equals(
415 ParamUtil.getString(request, "captchaText"));
416
417 if (valid) {
418 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
419 }
420
421 return valid;
422 }
423
424 protected boolean validateChallenge(PortletRequest portletRequest)
425 throws CaptchaException {
426
427 PortletSession portletSession = portletRequest.getPortletSession();
428
429 String captchaText = (String)portletSession.getAttribute(
430 WebKeys.CAPTCHA_TEXT);
431
432 if (captchaText == null) {
433 _log.error(
434 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
435 " may be trying to circumvent the CAPTCHA.");
436
437 throw new CaptchaTextException();
438 }
439
440 boolean valid = captchaText.equals(
441 ParamUtil.getString(portletRequest, "captchaText"));
442
443 if (valid) {
444 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
445 }
446
447 return valid;
448 }
449
450 private static final String _TAGLIB_PATH =
451 "/html/taglib/ui/captcha/simplecaptcha.jsp";
452
453 private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
454
455 private BackgroundProducer[] _backgroundProducers;
456 private GimpyRenderer[] _gimpyRenderers;
457 private NoiseProducer[] _noiseProducers;
458 private TextProducer[] _textProducers;
459 private WordRenderer[] _wordRenderers;
460
461 }