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 public void check(HttpServletRequest request) throws CaptchaException {
064 if (!isEnabled(request)) {
065 return;
066 }
067
068 if (!validateChallenge(request)) {
069 incrementCounter(request);
070
071 checkMaxChallenges(request);
072
073 throw new CaptchaTextException();
074 }
075
076 if (_log.isDebugEnabled()) {
077 _log.debug("CAPTCHA text is valid");
078 }
079 }
080
081 public void check(PortletRequest portletRequest) throws CaptchaException {
082 if (!isEnabled(portletRequest)) {
083 return;
084 }
085
086 if (!validateChallenge(portletRequest)) {
087 incrementCounter(portletRequest);
088
089 checkMaxChallenges(portletRequest);
090
091 throw new CaptchaTextException();
092 }
093
094 if (_log.isDebugEnabled()) {
095 _log.debug("CAPTCHA text is valid");
096 }
097 }
098
099 public String getTaglibPath() {
100 return _TAGLIB_PATH;
101 }
102
103 public boolean isEnabled(HttpServletRequest request)
104 throws CaptchaException {
105
106 checkMaxChallenges(request);
107
108 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
109 return true;
110 }
111 else {
112 return false;
113 }
114 }
115
116 public boolean isEnabled(PortletRequest portletRequest)
117 throws CaptchaException {
118
119 checkMaxChallenges(portletRequest);
120
121 if (PropsValues.CAPTCHA_MAX_CHALLENGES >= 0) {
122 return true;
123 }
124 else {
125 return false;
126 }
127 }
128
129 public void serveImage(
130 HttpServletRequest request, HttpServletResponse response)
131 throws IOException {
132
133 HttpSession session = request.getSession();
134
135 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
136
137 session.setAttribute(WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
138
139 response.setContentType(ContentTypes.IMAGE_JPEG);
140
141 CaptchaServletUtil.writeImage(
142 response.getOutputStream(), simpleCaptcha.getImage());
143 }
144
145 public void serveImage(
146 ResourceRequest resourceRequest, ResourceResponse resourceResponse)
147 throws IOException {
148
149 PortletSession portletSession = resourceRequest.getPortletSession();
150
151 nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha();
152
153 portletSession.setAttribute(
154 WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer());
155
156 CaptchaServletUtil.writeImage(
157 resourceResponse.getPortletOutputStream(),
158 simpleCaptcha.getImage());
159 }
160
161 protected void checkMaxChallenges(HttpServletRequest request)
162 throws CaptchaMaxChallengesException {
163
164 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
165 HttpSession session = request.getSession();
166
167 Integer count = (Integer)session.getAttribute(
168 WebKeys.CAPTCHA_COUNT);
169
170 checkMaxChallenges(count);
171 }
172 }
173
174 protected void checkMaxChallenges(Integer count)
175 throws CaptchaMaxChallengesException {
176
177 if ((count != null) && (count > PropsValues.CAPTCHA_MAX_CHALLENGES)) {
178 throw new CaptchaMaxChallengesException();
179 }
180 }
181
182 protected void checkMaxChallenges(PortletRequest portletRequest)
183 throws CaptchaMaxChallengesException {
184
185 if (PropsValues.CAPTCHA_MAX_CHALLENGES > 0) {
186 PortletSession portletSession = portletRequest.getPortletSession();
187
188 Integer count = (Integer)portletSession.getAttribute(
189 WebKeys.CAPTCHA_COUNT);
190
191 checkMaxChallenges(count);
192 }
193 }
194
195 protected BackgroundProducer getBackgroundProducer() {
196 if (_backgroundProducers.length == 1) {
197 return _backgroundProducers[0];
198 }
199
200 Randomizer randomizer = Randomizer.getInstance();
201
202 int pos = randomizer.nextInt(_backgroundProducers.length);
203
204 return _backgroundProducers[pos];
205 }
206
207 protected GimpyRenderer getGimpyRenderer() {
208 if (_gimpyRenderers.length == 1) {
209 return _gimpyRenderers[0];
210 }
211
212 Randomizer randomizer = Randomizer.getInstance();
213
214 int pos = randomizer.nextInt(_gimpyRenderers.length);
215
216 return _gimpyRenderers[pos];
217 }
218
219 protected int getHeight() {
220 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_HEIGHT;
221 }
222
223 protected NoiseProducer getNoiseProducer() {
224 if (_noiseProducers.length == 1) {
225 return _noiseProducers[0];
226 }
227
228 Randomizer randomizer = Randomizer.getInstance();
229
230 int pos = randomizer.nextInt(_noiseProducers.length);
231
232 return _noiseProducers[pos];
233 }
234
235 protected nl.captcha.Captcha getSimpleCaptcha() {
236 nl.captcha.Captcha.Builder captchaBuilder =
237 new nl.captcha.Captcha.Builder(getWidth(), getHeight());
238
239 captchaBuilder.addText(getTextProducer(), getWordRenderer());
240 captchaBuilder.addBackground(getBackgroundProducer());
241 captchaBuilder.gimp(getGimpyRenderer());
242 captchaBuilder.addNoise(getNoiseProducer());
243 captchaBuilder.addBorder();
244
245 return captchaBuilder.build();
246 }
247
248 protected TextProducer getTextProducer() {
249 if (_textProducers.length == 1) {
250 return _textProducers[0];
251 }
252
253 Randomizer randomizer = Randomizer.getInstance();
254
255 int pos = randomizer.nextInt(_textProducers.length);
256
257 return _textProducers[pos];
258 }
259
260 protected int getWidth() {
261 return PropsValues.CAPTCHA_ENGINE_SIMPLECAPTCHA_WIDTH;
262 }
263
264 protected WordRenderer getWordRenderer() {
265 if (_wordRenderers.length == 1) {
266 return _wordRenderers[0];
267 }
268
269 Randomizer randomizer = Randomizer.getInstance();
270
271 int pos = randomizer.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 (captchaText == null) {
396 _log.error(
397 "CAPTCHA text is null. User " + request.getRemoteUser() +
398 " may be trying to circumvent the CAPTCHA.");
399
400 throw new CaptchaTextException();
401 }
402
403 boolean valid = captchaText.equals(
404 ParamUtil.getString(request, "captchaText"));
405
406 if (valid) {
407 session.removeAttribute(WebKeys.CAPTCHA_TEXT);
408 }
409
410 return valid;
411 }
412
413 protected boolean validateChallenge(PortletRequest portletRequest)
414 throws CaptchaException {
415
416 PortletSession portletSession = portletRequest.getPortletSession();
417
418 String captchaText = (String)portletSession.getAttribute(
419 WebKeys.CAPTCHA_TEXT);
420
421 if (captchaText == null) {
422 _log.error(
423 "CAPTCHA text is null. User " + portletRequest.getRemoteUser() +
424 " may be trying to circumvent the CAPTCHA.");
425
426 throw new CaptchaTextException();
427 }
428
429 boolean valid = captchaText.equals(
430 ParamUtil.getString(portletRequest, "captchaText"));
431
432 if (valid) {
433 portletSession.removeAttribute(WebKeys.CAPTCHA_TEXT);
434 }
435
436 return valid;
437 }
438
439 private static final String _TAGLIB_PATH =
440 "/html/taglib/ui/captcha/simplecaptcha.jsp";
441
442 private static Log _log = LogFactoryUtil.getLog(SimpleCaptchaImpl.class);
443
444 private BackgroundProducer[] _backgroundProducers;
445 private GimpyRenderer[] _gimpyRenderers;
446 private NoiseProducer[] _noiseProducers;
447 private TextProducer[] _textProducers;
448 private WordRenderer[] _wordRenderers;
449
450 }