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.util.ContentTypes;
025 import com.liferay.portal.kernel.util.InstancePool;
026 import com.liferay.portal.kernel.util.ParamUtil;
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 int pos = RandomUtil.nextInt(_backgroundProducers.length);
210
211 return _backgroundProducers[pos];
212 }
213
214 protected GimpyRenderer getGimpyRenderer() {
215 if (_gimpyRenderers.length == 1) {
216 return _gimpyRenderers[0];
217 }
218
219 int pos = RandomUtil.nextInt(_gimpyRenderers.length);
220
221 return _gimpyRenderers[pos];
222 }
223
224 protected int getHeight() {
225 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
226 }
227
228 protected NoiseProducer getNoiseProducer() {
229 if (_noiseProducers.length == 1) {
230 return _noiseProducers[0];
231 }
232
233 int pos = RandomUtil.nextInt(_noiseProducers.length);
234
235 return _noiseProducers[pos];
236 }
237
238 protected nl.captcha.Captcha getSimpleCaptcha() {
239 nl.captcha.Captcha.Builder captchaBuilder =
240 new nl.captcha.Captcha.Builder(getWidth(), getHeight());
241
242 captchaBuilder.addText(getTextProducer(), getWordRenderer());
243 captchaBuilder.addBackground(getBackgroundProducer());
244 captchaBuilder.gimp(getGimpyRenderer());
245 captchaBuilder.addNoise(getNoiseProducer());
246 captchaBuilder.addBorder();
247
248 return captchaBuilder.build();
249 }
250
251 protected TextProducer getTextProducer() {
252 if (_textProducers.length == 1) {
253 return _textProducers[0];
254 }
255
256 int pos = RandomUtil.nextInt(_textProducers.length);
257
258 return _textProducers[pos];
259 }
260
261 protected int getWidth() {
262 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
263 }
264
265 protected WordRenderer getWordRenderer() {
266 if (_wordRenderers.length == 1) {
267 return _wordRenderers[0];
268 }
269
270 int pos = RandomUtil.nextInt(_wordRenderers.length);
271
272 return _wordRenderers[pos];
273 }
274
275 protected void incrementCounter(HttpServletRequest request) {
276 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
277 Validator.isNotNull(request.getRemoteUser())) {
278
279 HttpSession session = request.getSession();
280
281 Integer count = (Integer)session.getAttribute(
282 WebKeys.CAPTCHA_COUNT);
283
284 session.setAttribute(
285 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
286 }
287 }
288
289 protected Integer incrementCounter(Integer count) {
290 if (count == null) {
291 count = new Integer(1);
292 }
293 else {
294 count = new Integer(count.intValue() + 1);
295 }
296
297 return count;
298 }
299
300 protected void incrementCounter(PortletRequest portletRequest) {
301 if ((PropsValues.CAPTCHA_MAX_CHALLENGES > 0) &&
302 Validator.isNotNull(portletRequest.getRemoteUser())) {
303
304 PortletSession portletSession = portletRequest.getPortletSession();
305
306 Integer count = (Integer)portletSession.getAttribute(
307 WebKeys.CAPTCHA_COUNT);
308
309 portletSession.setAttribute(
310 WebKeys.CAPTCHA_COUNT, incrementCounter(count));
311 }
312 }
313
314 protected void initBackgroundProducers() {
315 String[] backgroundProducerClassNames =
316 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_BACKGROUND_PRODUCERS;
317
318 _backgroundProducers = new BackgroundProducer[
319 backgroundProducerClassNames.length];
320
321 for (int i = 0; i < backgroundProducerClassNames.length; i++) {
322 String backgroundProducerClassName =
323 backgroundProducerClassNames[i];
324
325 _backgroundProducers[i] = (BackgroundProducer)InstancePool.get(
326 backgroundProducerClassName);
327 }
328 }
329
330 protected void initGimpyRenderers() {
331 String[] gimpyRendererClassNames =
332 PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_GIMPY_RENDERERS;
333
334 _gimpyRenderers = new GimpyRenderer[
335 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 (captchaText == null) {
395 _log.error(
396 "CAPTCHA text is null. User " + request.getRemoteUser() +
397 " may be trying to circumvent the CAPTCHA.");
398
399 throw new CaptchaTextException();
400 }
401
402 boolean valid = captchaText.equals(
403 ParamUtil.getString(request, "captchaText"));
404
405 if (valid) {
406 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
407 }
408
409 return valid;
410 }
411
412 protected boolean validateChallenge(PortletRequest portletRequest)
413 throws CaptchaException {
414
415 PortletSession portletSession = portletRequest.getPortletSession();
416
417 String captchaText = (String)portletSession.getAttribute(
418 WebKeys.CAPTCHA_TEXT);
419
420 if (captchaText == null) {
421 _log.error(
422 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
423 " may be trying to circumvent the CAPTCHA.");
424
425 throw new CaptchaTextException();
426 }
427
428 boolean valid = captchaText.equals(
429 ParamUtil.getString(portletRequest, "captchaText"));
430
431 if (valid) {
432 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
433 }
434
435 return valid;
436 }
437
438 private static final String _TAGLIB_PATH =
439 "/html/taglib/ui/captcha/simplecaptcha.jsp";
440
441 private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
442
443 private BackgroundProducer[] _backgroundProducers;
444 private GimpyRenderer[] _gimpyRenderers;
445 private NoiseProducer[] _noiseProducers;
446 private TextProducer[] _textProducers;
447 private WordRenderer[] _wordRenderers;
448
449 }