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