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.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.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 = new Integer(1);
293 }
294 else {
295 count = new Integer(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[
336 gimpyRendererClassNames.length];
337
338 for (int i = 0; i < gimpyRendererClassNames.length; i++) {
339 String gimpyRendererClassName = gimpyRendererClassNames[i];
340
341 _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
342 gimpyRendererClassName);
343 }
344 }
345
346 protected void initNoiseProducers() {
347 String[] noiseProducerClassNames =
348 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
349
350 _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
351
352 for (int i = 0; i < noiseProducerClassNames.length; i++) {
353 String noiseProducerClassName = noiseProducerClassNames[i];
354
355 _noiseProducers[i] = (NoiseProducer)InstancePool.get(
356 noiseProducerClassName);
357 }
358 }
359
360 protected void initTextProducers() {
361 String[] textProducerClassNames =
362 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
363
364 _textProducers = new TextProducer[textProducerClassNames.length];
365
366 for (int i = 0; i < textProducerClassNames.length; i++) {
367 String textProducerClassName = textProducerClassNames[i];
368
369 _textProducers[i] = (TextProducer)InstancePool.get(
370 textProducerClassName);
371 }
372 }
373
374 protected void initWordRenderers() {
375 String[] wordRendererClassNames =
376 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
377
378 _wordRenderers = new WordRenderer[wordRendererClassNames.length];
379
380 for (int i = 0; i < wordRendererClassNames.length; i++) {
381 String wordRendererClassName = wordRendererClassNames[i];
382
383 _wordRenderers[i] = (WordRenderer)InstancePool.get(
384 wordRendererClassName);
385 }
386 }
387
388 protected boolean validateChallenge(HttpServletRequest request)
389 throws CaptchaException {
390
391 HttpSession session = request.getSession();
392
393 String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
394
395 if (request instanceof UploadPortletRequest) {
396 UploadPortletRequest uploadPortletRequest =
397 (UploadPortletRequest)request;
398
399 PortletRequest portletRequest =
400 uploadPortletRequest.getPortletRequest();
401
402 PortletSession portletSession = portletRequest.getPortletSession();
403
404 captchaText = (String)portletSession.getAttribute(
405 WebKeys.CAPTCHA_TEXT);
406 }
407
408 if (captchaText == null) {
409 _log.error(
410 "CAPTCHA text is null. User " + request.getRemoteUser() +
411 " may be trying to circumvent the CAPTCHA.");
412
413 throw new CaptchaTextException();
414 }
415
416 boolean valid = captchaText.equals(
417 ParamUtil.getString(request, "captchaText"));
418
419 if (valid) {
420 if (request instanceof UploadPortletRequest) {
421 UploadPortletRequest uploadPortletRequest =
422 (UploadPortletRequest)request;
423
424 PortletRequest portletRequest =
425 uploadPortletRequest.getPortletRequest();
426
427 PortletSession portletSession =
428 portletRequest.getPortletSession();
429
430 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
431 }
432 else {
433 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
434 }
435 }
436
437 return valid;
438 }
439
440 protected boolean validateChallenge(PortletRequest portletRequest)
441 throws CaptchaException {
442
443 PortletSession portletSession = portletRequest.getPortletSession();
444
445 String captchaText = (String)portletSession.getAttribute(
446 WebKeys.CAPTCHA_TEXT);
447
448 if (captchaText == null) {
449 _log.error(
450 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
451 " may be trying to circumvent the CAPTCHA.");
452
453 throw new CaptchaTextException();
454 }
455
456 boolean valid = captchaText.equals(
457 ParamUtil.getString(portletRequest, "captchaText"));
458
459 if (valid) {
460 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
461 }
462
463 return valid;
464 }
465
466 private static final String _TAGLIB_PATH =
467 "/html/taglib/ui/captcha/simplecaptcha.jsp";
468
469 private static Log _log = LogFactoryUtil.getLog(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 }