Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Jakarta-turbine Wiki" 
for change notification.

The following page has been changed by ZoltanZidarics:
http://wiki.apache.org/jakarta-turbine/Turbine2/Tutorial/CaptchaService

New page:
= Integrating Captcha Service into Turbine =

If you want to enable anonymous data input in your Turbine application like 
user registration and etc. you need to protect yourself from spammer robots.
There are a lot of captcha (Completely Automated Public Test to tell Computers 
and Humans Apart) modul, I prefer JCaptcha from 
http://jcaptcha.sourceforge.net/.
It has a lot of parameters like:
  *  character set for generation
  * min and max length of text
  * image width and height in pixels
  * font min and max size
  * font color

All of these parameters can be setting in TR.properties:
{{{
services.CaptchaService.classname=com.zamek.portal_zamek.services.captcha.TurbineCaptchaService
services.CaptchaService.earlyInit=true
services.CaptchaService.chars=ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789   #character 
set
services.CaptchaService.parser.min=5   #minimum length of captcha text
services.CaptchaService.parser.max=6  #maximun length of captcha text
services.CaptchaService.size.width=200  #width of captcha image (px)
services.CaptchaService.size.height=100 #height of captcha image (px)
services.CaptchaService.font.min=25  #minimum size of captcha text font (px)
services.CaptchaService.font.max=30 #maximum size of captcha text font (px)
services.CaptchaService.fontcolor=blue #color of captcha text 
}}}

== Using it on Velocity forms ==
If you need to create an anonymous form, you can call a macro like this:
{{{
#macro (formCaptchaField)
    #if (! $data.User.hasLoggedIn())
        <tr>
            <td colspan="2">
                <div align="center">
                <img src="$link.setAction('CaptchaAction').toString()" 
border="0">
                </div>
            </td>
        </tr>
        <tr>
            <td>
                Please enter the preceeding text
            </td>
            <td width="$inputWidth" class="frminput">
                <input type="text" maxlength="10" name="captcha_response" 
value="">
            </td>
        </tr>
    #end
#end
}}}

if user has not logged in, captcha is inserting into the form. And we need an 
action named CaptchaAction to serve captcha challenge images to form. It is a 
descendant of VelocityAction:
{{{
package com.zamek.portal_zamek.modules.actions;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.turbine.util.RunData;
import org.apache.velocity.context.Context;

import com.zamek.portal_zamek.ZamekConst;
import com.zamek.portal_zamek.services.captcha.TurbineCaptcha;

public class CaptchaAction extends VelocityAction
{
   private final static String LOGHEADER = 
"com.zamek.portal_zamek.modules.actions.";

   public void doPerform(RunData data, Context ctx) throws Exception
   {
       data.declareDirectResponse();  // declaring directresponse layout
       data.setLayout("DirectResponseLayout");
       processRequest(data);
   }

   private void processRequest(RunData data) throws ServletException, 
IOException
   {
        HttpServletResponse response = data.getResponse();
        response.setContentType("image/jpeg");
        try
       {
            String captchaId = data.getSession().getId();
            Locale locale = data.getLocale();
            ByteArrayOutputStream jpegOutputStream =
                   TurbineCaptcha.getImage(captchaId, locale);
            response.setContentLength(jpegOutputStream.size());
            jpegOutputStream.writeTo(response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error(LOGHEADER+" error:"+e.getMessage());
        }
   }
}
}}}

== Checking response ==
Service stores session id and original text, and if you get back the form data 
you can check it like this:
{{{
    public void doInsert (RunData data, Context context) {
        try
        {
            if (TurbineCaptcha.checkResponse(data)) {
                 codes to match response
            }
            else
                 setTemplate(data, FRM_REG);  // response doesn' t match
        }
        catch (CaptchaServiceException ce)
        {
            setTemplate(data, FRM_REG);
            
data.addMessage(Localization.getString(ZamekConst.BUNDLE_CAPTCHA_ERROR));
        }
    }
}}}

== The Service package ==
Finally we need to make captcha service package. There are 5 java class in 
package:
  * CaptchaService.java interface with Constant declarations
  * TurbineCaptchaService.java the core service code
  * TurbineCaptcha.java     static adapter to service
  * CaptchaGenerator.java extends DefaultManageableImageCaptchaService
  * CaptchaEngine.java  extends ListImageCaptchaEngine implements parameter 
settings

=== CaptchaService.java ===
{{{
package com.zamek.portal_zamek.services.captcha;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import org.apache.turbine.services.Service;
import org.apache.turbine.util.RunData;

public interface CaptchaService extends Service {

    /**
     * The service identifier
     */
    public final static String SERVICE_NAME = "CaptchaService";

    /**
     * chars key in config
     */
    public final static String CHARS_KEY = "chars";

    /**
     * default chars
     */
    public final static String DEFAULT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /**
     * min text length key in config
     */
    public final static String PARSER_MIN_KEY = "parser.min";

    /**
     * default min length
     */
    public final static int DEFAULT_PARSER_MIN = 5;

    /**
     * max text length key in config
     */
    public final static String PARSER_MAX_KEY = "parser.max";

    /**
     * default max length
     */
     public final static int DEFAULT_PARSER_MAX = 8;

    /**
     * size width key in config
     */
    public final static String SIZE_WIDTH_KEY = "size.width";
    
    /**
     * default size width
     */
    public final static int DEFAULT_SIZE_WIDTH = 200;

    /**
     * size height key in config
     */
    public final static String SIZE_HEIGHT_KEY = "size.height";
    
    /**
     * default size height
     */
    public final static int DEFAULT_SIZE_HEIGHT = 100;
        
    /**
     * min font size key in config
     */
    public final static String MIN_FONT_KEY = "font.min";
    
    /**
     * default font min
     */
    public final static int DEFAULT_FONT_MIN = 25;
    
    /**
     * max font size key in config
     */
    public final static String MAX_FONT_KEY = "font.max";
    
    /**
     * default font max
     */
    public final static int DEFAULT_FONT_MAX = 30;

    /**
     * font color key in config
     */
    public final static String FONT_COLOR_KEY = "fontcolor";
    
    /**
     * default font color
     */
    public final static String DEFAULT_FONT_COLOR = "white";
    
    /**
     * name of captcha field on form
     */
    public final static String CTX_CAPTCHA = "captcha";
    
    /**
     * name of captcha response field on form
     */
    public final static String CTX_CAPTCHA_RESPONSE = "captcha_response";
    
    /**
     * Captcha error message form localized messages
     */
    public final static String BUNDLE_CAPTCHA_ERROR = "captchaError";
    
    
    /**
     * getting a new captcha
     * @param captchaId session id
     * @param locale locales for request
     * @return an image
     */
    public BufferedImage getCaptcha(final String captchaId, final Locale 
locale);

    /**
     * getting a captcha image
     * @param captchaId session id
     * @param locale locales for request
     * @return an jpeg stream
     */
    public ByteArrayOutputStream getImage(final String captchaId, final Locale 
locale) throws IOException;
    
    
    /**
     * check response
     * @param data
     * @return
     */
    public boolean checkResponse (RunData data);

    /**
     * validating response for session id
     * @param id session id
     * @param response text of response
     * @return
     */
    public boolean validateResponseForID(final String id, final String 
response);

}

}}}
=== TurbineCaptchaService.java ===
{{{
package com.zamek.portal_zamek.services.captcha;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.localization.Localization;
import org.apache.turbine.util.RunData;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class TurbineCaptchaService extends TurbineBaseService 
    implements    CaptchaService {

    protected Log log = LogFactory.getLog(TurbineCaptchaService.class);
    private final static String 
LOGHEADER="com.zamek.portal_zamek.services.captcha.";

   private CaptchaGenerator generator = null;

    /**
     * initialize service
     */
    public void init() throws InitializationException {
        generator = new CaptchaGenerator();
        setInit(true);
        log.debug(LOGHEADER+"init ok");
    }

    /**
     * getting a new captcha
     * @param captchaId session id
     * @param locale locales for request
     * @return an image
     */
    public BufferedImage getCaptcha(final String captchaId, final Locale locale)
    {
        if (log.isDebugEnabled())
            log.debug(String.format(
                    LOGHEADER + "getCaptcha enter, captchaId:%s, locale:%s",
                    captchaId, locale));
        try {
            BufferedImage challenge =
                  generator.getImageChallengeForID(captchaId, locale);
            return challenge;
        }
        catch (Exception e) {
          log.error(LOGHEADER + e.getMessage());
          return null;
        }
    }

    /**
     * check response
     * @param data
     * @return
     */
    public boolean checkResponse(RunData data)
    {
        String id = data.getSession().getId(),
           response = 
data.getParameters().get(CaptchaService.CTX_CAPTCHA_RESPONSE);
        if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(response) &&
                validateResponseForID(id, response))
            return true;
        
data.addMessage(Localization.getString(CaptchaService.BUNDLE_CAPTCHA_ERROR));
        return false;
    }
    
    /**
     * validating response for session id
     * @param id session id
     * @param response text of response
     * @return
     */
    public boolean validateResponseForID(final String id, final String response)
    {
        return generator.validateResponseForID(id, response);
    }
    
    /**
     *
     * @param captchaId session id
     * @param locale locales for request
     * @return an jpeg stream
     */
    public ByteArrayOutputStream getImage(final String captchaId, final Locale 
locale)
        throws IOException
    {
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        BufferedImage challenge = this.getCaptcha(captchaId, locale);
        // a jpeg encoder
        JPEGImageEncoder jpegEncoder =
              JPEGCodec.createJPEGEncoder(jpegOutputStream);
        jpegEncoder.encode(challenge);
        return jpegOutputStream;
    
    }
}
}}}
=== TurbineCaptcha.java ===
{{{
package com.zamek.portal_zamek.services.captcha;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;

import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.util.RunData;

public class TurbineCaptcha {

    /**
     * get service object
     * @return
     */
    public static CaptchaService getService()
    {
        return (CaptchaService) TurbineServices.getInstance().
                getService(CaptchaService.SERVICE_NAME);
    }

    /**
     * checking service available
     * @return
     * @throws InstantiationException
     */
    public static boolean isAvailable() throws InstantiationException
    {
        try
        {
            @SuppressWarnings("unused")
            CaptchaService captcha = getService();
            return true;
        }
        catch (Exception ie) {
            return false;
        }
    }
    
    /**
     * getting a captcha challenge image
     * @param captchaId
     * @param locale
     * @return
     */
    public static BufferedImage getCaptcha(final String captchaId, final Locale 
locale)
    {
        return getService().getCaptcha(captchaId, locale);
    }

    /**
     * checking response of user
     * @param data
     * @return
     */
    public static boolean checkResponse(RunData data)
    {
        return getService().checkResponse(data);
    }
    
    /**
     * validate user's response to compare stored text
     * @param id
     * @param response
     * @return
     */
    public static boolean validateResponseForID(final String id, final String 
response)
    {
        return getService().validateResponseForID(id, response);
    }
    
    /**
     * getting a challenge image
     * @param captchaId
     * @param locale
     * @return
     * @throws IOException
     */
    public static ByteArrayOutputStream getImage(final String captchaId, final 
Locale locale)
        throws IOException
    {
        return getService().getImage(captchaId, locale);
    }

}
}}}
=== CaptchaGenerator.java ===
{{{
package com.zamek.portal_zamek.services.captcha;

import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;

public class CaptchaGenerator extends DefaultManageableImageCaptchaService {

    public CaptchaGenerator() {
        super();
        this.setCaptchaEngine(new CaptchaEngine());
    }
}
}}}
=== CaptchaEngine.java ===
{{{
package com.zamek.portal_zamek.services.captcha;

import java.awt.Color;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.turbine.Turbine;
import com.octo.captcha.component.image.backgroundgenerator.BackgroundGenerator;
import 
com.octo.captcha.component.image.backgroundgenerator.FunkyBackgroundGenerator;
import com.octo.captcha.component.image.fontgenerator.FontGenerator;
import 
com.octo.captcha.component.image.fontgenerator.TwistedAndShearedRandomFontGenerator;
import com.octo.captcha.component.image.textpaster.RandomTextPaster;
import com.octo.captcha.component.image.textpaster.TextPaster;
import com.octo.captcha.component.image.wordtoimage.ComposedWordToImage;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.image.gimpy.GimpyFactory;

public class CaptchaEngine extends ListImageCaptchaEngine
{
    private final static String LOGHEADER = 
"com.zamek.portal_zamek.services.captcha.";
    private final static Log log = LogFactory.getLog(CaptchaEngine.class);
    
    @Override
    protected void buildInitialFactories()
    {
        Configuration conf = Turbine.getConfiguration();
        int parserMin = conf.getInt(
                    CaptchaService.PARSER_MIN_KEY, 
CaptchaService.DEFAULT_PARSER_MIN),
            parserMax = conf.getInt(
                    CaptchaService.PARSER_MAX_KEY, 
CaptchaService.DEFAULT_PARSER_MAX),
            sizeWidth = conf.getInt(
                    CaptchaService.SIZE_WIDTH_KEY, 
CaptchaService.DEFAULT_SIZE_WIDTH),
            sizeHeight = conf.getInt(
                    CaptchaService.SIZE_HEIGHT_KEY, 
CaptchaService.DEFAULT_SIZE_HEIGHT),
            fontMin = conf.getInt(
                    CaptchaService.MIN_FONT_KEY, 
CaptchaService.DEFAULT_FONT_MIN),
            fontMax = conf.getInt(
                    CaptchaService.MAX_FONT_KEY, 
CaptchaService.DEFAULT_FONT_MAX);
            String chars = conf.getString(
                    CaptchaService.CHARS_KEY, CaptchaService.DEFAULT_CHARS),
                   colorName = conf.getString(
                    CaptchaService.FONT_COLOR_KEY, 
CaptchaService.DEFAULT_FONT_COLOR);
            Color fontColor = Color.getColor(colorName);
            if (log.isDebugEnabled())
                log.debug(LOGHEADER +
                        String.format("init, parserMin:%d, parserMax:%d, 
sizeWidth:%d, sizeHeight:%d, fontMin:%d, fontMax:%d, chars:%s",
                                      parserMin, parserMax, sizeWidth, 
sizeHeight, fontMin, fontMax, chars));
            WordGenerator wordGenerator = new RandomWordGenerator(chars);
    
            TextPaster textParser = new RandomTextPaster(parserMin, parserMax, 
fontColor);
    
            BackgroundGenerator backgroundGenerator =
                new FunkyBackgroundGenerator (sizeWidth, sizeHeight);
    
            FontGenerator fontGenerator =
                new TwistedAndShearedRandomFontGenerator (fontMin, fontMax);
    
            WordToImage wordToImage = new ComposedWordToImage(fontGenerator, 
backgroundGenerator, textParser);
            this.addFactory(new GimpyFactory(wordGenerator, wordToImage));
    }
}
}}}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to