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.PropsValues;
029 import com.liferay.portal.util.WebKeys;
030
031 import java.io.IOException;
032
033 import javax.portlet.PortletRequest;
034 import javax.portlet.PortletSession;
035 import javax.portlet.ResourceRequest;
036 import javax.portlet.ResourceResponse;
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 ResourceRequest resourceRequest, ResourceResponse resourceResponse)
154 throws IOException {
155
156 PortletSession portletSession = resourceRequest.getPortletSession();
157
158 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
159
160 portletSession.setAttribute(
161 WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
162
163 resourceResponse.setContentType(ContentTypes.IMAGE_PNG);
164
165 CaptchaServletUtil.writeImage(
166 resourceResponse.getPortletOutputStream(),
167 simpleCaptcha.getImage());
168 }
169
170 protected void checkMaxChallenges(HttpServletRequest request)
171 throws CaptchaMaxChallengesException {
172
173 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
174 HttpSession session = request.getSession();
175
176 Integer count = (Integer)session.getAttribute(
177 WebKeys.CAPTCHA_COUNT);
178
179 checkMaxChallenges(count);
180 }
181 }
182
183 protected void checkMaxChallenges(Integer count)
184 throws CaptchaMaxChallengesException {
185
186 if ((count != null) && (count > PropsValues.CAPTCHA_MAX_CHALLENGES)) {
187 throw new CaptchaMaxChallengesException();
188 }
189 }
190
191 protected void checkMaxChallenges(PortletRequest portletRequest)
192 throws CaptchaMaxChallengesException {
193
194 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
195 PortletSession portletSession = portletRequest.getPortletSession();
196
197 Integer count = (Integer)portletSession.getAttribute(
198 WebKeys.CAPTCHA_COUNT);
199
200 checkMaxChallenges(count);
201 }
202 }
203
204 protected BackgroundProducer getBackgroundProducer() {
205 if (_backgroundProducers.length == 1) {
206 return _backgroundProducers[0];
207 }
208
209 Randomizer randomizer = Randomizer.getInstance();
210
211 int pos = randomizer.nextInt(_backgroundProducers.length);
212
213 return _backgroundProducers[pos];
214 }
215
216 protected GimpyRenderer getGimpyRenderer() {
217 if (_gimpyRenderers.length == 1) {
218 return _gimpyRenderers[0];
219 }
220
221 Randomizer randomizer = Randomizer.getInstance();
222
223 int pos = randomizer.nextInt(_gimpyRenderers.length);
224
225 return _gimpyRenderers[pos];
226 }
227
228 protected int getHeight() {
229 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
230 }
231
232 protected NoiseProducer getNoiseProducer() {
233 if (_noiseProducers.length == 1) {
234 return _noiseProducers[0];
235 }
236
237 Randomizer randomizer = Randomizer.getInstance();
238
239 int pos = randomizer.nextInt(_noiseProducers.length);
240
241 return _noiseProducers[pos];
242 }
243
244 protected nl.captcha.Captcha getSimpleCaptcha() {
245 nl.captcha.Captcha.Builder captchaBuilder =
246 new nl.captcha.Captcha.Builder(getWidth(), getHeight());
247
248 captchaBuilder.addText(getTextProducer(), getWordRenderer());
249 captchaBuilder.addBackground(getBackgroundProducer());
250 captchaBuilder.gimp(getGimpyRenderer());
251 captchaBuilder.addNoise(getNoiseProducer());
252 captchaBuilder.addBorder();
253
254 return captchaBuilder.build();
255 }
256
257 protected TextProducer getTextProducer() {
258 if (_textProducers.length == 1) {
259 return _textProducers[0];
260 }
261
262 Randomizer randomizer = Randomizer.getInstance();
263
264 int pos = randomizer.nextInt(_textProducers.length);
265
266 return _textProducers[pos];
267 }
268
269 protected int getWidth() {
270 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
271 }
272
273 protected WordRenderer getWordRenderer() {
274 if (_wordRenderers.length == 1) {
275 return _wordRenderers[0];
276 }
277
278 Randomizer randomizer = Randomizer.getInstance();
279
280 int pos = randomizer.nextInt(_wordRenderers.length);
281
282 return _wordRenderers[pos];
283 }
284
285 protected void incrementCounter(HttpServletRequest request) {
286 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
287 Validator.isNotNull(request.getRemoteUser())) {
288
289 HttpSession session = request.getSession();
290
291 Integer count = (Integer)session.getAttribute(
292 WebKeys.CAPTCHA_COUNT);
293
294 session.setAttribute(
295 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
296 }
297 }
298
299 protected Integer incrementCounter(Integer count) {
300 if (count == null) {
301 count = new Integer(1);
302 }
303 else {
304 count = new Integer(count.intValue() + 1);
305 }
306
307 return count;
308 }
309
310 protected void incrementCounter(PortletRequest portletRequest) {
311 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
312 Validator.isNotNull(portletRequest.getRemoteUser())) {
313
314 PortletSession portletSession = portletRequest.getPortletSession();
315
316 Integer count = (Integer)portletSession.getAttribute(
317 WebKeys.CAPTCHA_COUNT);
318
319 portletSession.setAttribute(
320 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
321 }
322 }
323
324 protected void initBackgroundProducers() {
325 String[] backgroundProducerClassNames =
326 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
327
328 _backgroundProducers = new BackgroundProducer[
329 backgroundProducerClassNames.length];
330
331 for (int i = 0; i < backgroundProducerClassNames.length; i++) {
332 String backgroundProducerClassName =
333 backgroundProducerClassNames[i];
334
335 _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
336 backgroundProducerClassName);
337 }
338 }
339
340 protected void initGimpyRenderers() {
341 String[] gimpyRendererClassNames =
342 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
343
344 _gimpyRenderers = new GimpyRenderer[
345 gimpyRendererClassNames.length];
346
347 for (int i = 0; i < gimpyRendererClassNames.length; i++) {
348 String gimpyRendererClassName = gimpyRendererClassNames[i];
349
350 _gimpyRenderers[i] = (GimpyRenderer)InstancePool.get(
351 gimpyRendererClassName);
352 }
353 }
354
355 protected void initNoiseProducers() {
356 String[] noiseProducerClassNames =
357 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_NOISE_PRODUCERS;
358
359 _noiseProducers = new NoiseProducer[noiseProducerClassNames.length];
360
361 for (int i = 0; i < noiseProducerClassNames.length; i++) {
362 String noiseProducerClassName = noiseProducerClassNames[i];
363
364 _noiseProducers[i] = (NoiseProducer)InstancePool.get(
365 noiseProducerClassName);
366 }
367 }
368
369 protected void initTextProducers() {
370 String[] textProducerClassNames =
371 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_TEXT_PRODUCERS;
372
373 _textProducers = new TextProducer[textProducerClassNames.length];
374
375 for (int i = 0; i < textProducerClassNames.length; i++) {
376 String textProducerClassName = textProducerClassNames[i];
377
378 _textProducers[i] = (TextProducer)InstancePool.get(
379 textProducerClassName);
380 }
381 }
382
383 protected void initWordRenderers() {
384 String[] wordRendererClassNames =
385 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WORD_RENDERERS;
386
387 _wordRenderers = new WordRenderer[wordRendererClassNames.length];
388
389 for (int i = 0; i < wordRendererClassNames.length; i++) {
390 String wordRendererClassName = wordRendererClassNames[i];
391
392 _wordRenderers[i] = (WordRenderer)InstancePool.get(
393 wordRendererClassName);
394 }
395 }
396
397 protected boolean validateChallenge(HttpServletRequest request)
398 throws CaptchaException {
399
400 HttpSession session = request.getSession();
401
402 String captchaText = (String)session.getAttribute(WebKeys.CAPTCHA_TEXT);
403
404 if (captchaText == null) {
405 _log.error(
406 "CAPTCHA text is null. User " + request.getRemoteUser() +
407 " may be trying to circumvent the CAPTCHA.");
408
409 throw new CaptchaTextException();
410 }
411
412 boolean valid = captchaText.equals(
413 ParamUtil.getString(request, "captchaText"));
414
415 if (valid) {
416 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
417 }
418
419 return valid;
420 }
421
422 protected boolean validateChallenge(PortletRequest portletRequest)
423 throws CaptchaException {
424
425 PortletSession portletSession = portletRequest.getPortletSession();
426
427 String captchaText = (String)portletSession.getAttribute(
428 WebKeys.CAPTCHA_TEXT);
429
430 if (captchaText == null) {
431 _log.error(
432 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
433 " may be trying to circumvent the CAPTCHA.");
434
435 throw new CaptchaTextException();
436 }
437
438 boolean valid = captchaText.equals(
439 ParamUtil.getString(portletRequest, "captchaText"));
440
441 if (valid) {
442 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
443 }
444
445 return valid;
446 }
447
448 private static final String _TAGLIB_PATH =
449 "/html/taglib/ui/captcha/simplecaptcha.jsp";
450
451 private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
452
453 private BackgroundProducer[] _backgroundProducers;
454 private GimpyRenderer[] _gimpyRenderers;
455 private NoiseProducer[] _noiseProducers;
456 private TextProducer[] _textProducers;
457 private WordRenderer[] _wordRenderers;
458
459 }