/* ===============================================================================
*
* Part of the InfoGlue Content Management Platform (www.infoglue.org)
*
* ===============================================================================
*
* Copyright (C)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2, as published by the
* Free Software Foundation. See the file LICENSE.html for more information.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
* Place, Suite 330 / Boston, MA 02111-1307 / USA.
*
* ===============================================================================
*/
package org.infoglue.calendar.taglib;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import org.apache.log4j.Logger;
import org.infoglue.calendar.util.graphics.AdvancedImageRenderer;
import org.infoglue.common.util.PropertyHelper;
/**
* A Tag used for rendering distorted images with random text.
* @author Tommy Berglund <a href="mailto:tommy.berglund@hotmail.com">tommy.berglund@modul1.se</a>
*/
public class GapchaTag extends AbstractTag
{
private final static Logger logger = Logger.getLogger(GapchaTag.class.getName());
/**
*
*/
private static final long serialVersionUID = 1L;
private String allowedCharacters = "abcdefghijklmonpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private Map renderAttributes = null;
private Object result;
private String textVariableName = "CAPTHCA_TEXT";
private int numberOfCharacters = 5;
private static int requestsNO = 0;
public GapchaTag()
{
super();
}
public int doEndTag() throws JspException
{
if(requestsNO > 50)
{
cleanOldFiles();
requestsNO = 0;
}
// create the random string
char[] randomCharacters = createRandomCharacters();
// set the random string in the session
String sessionVariableName = textVariableName + "_" + System.currentTimeMillis();
pageContext.getSession().setAttribute( sessionVariableName, new String(randomCharacters) );
pageContext.getSession().setAttribute( "useCaptchaForEntry", "true" );
pageContext.setAttribute(textVariableName, sessionVariableName);
// without spacing it is really hard to read the text
String randomText = spaceCharacters(randomCharacters);
try
{
result = this.getRenderedTextUrl( randomText, renderAttributes, true );
}
catch (Exception e)
{
e.printStackTrace();
}
this.produceResult( result );
requestsNO++;
return EVAL_PAGE;
}
private String getRenderedTextUrl( String text, Map renderAttributes, boolean distort )
{
String assetUrl = "";
if ( text == null || text.length() == 0 )
{
logger.warn("Could not render text with a null or 0 lenght value");
return assetUrl;
}
try
{
String aText = text.replaceAll( "[^\\w]", "" );
aText = aText.substring( 0, ( aText.length() < 12 ? aText.length() : 11 ) ).toLowerCase();
StringBuffer uniqueId = new StringBuffer( aText );
uniqueId.append( "_" + Math.abs( text.hashCode() ) );
uniqueId.append( "_" + ( renderAttributes != null ? Math.abs( renderAttributes.hashCode() ) : 4711 ) );
AdvancedImageRenderer imageRenderer = new AdvancedImageRenderer();
// render the image
imageRenderer.renderImage( text, renderAttributes );
if( distort )
{
uniqueId = new StringBuffer( "igcaptcha" );
uniqueId.append( "_" + Math.abs( text.hashCode() ) );
uniqueId.append( "_" + ( renderAttributes != null ? Math.abs( renderAttributes.hashCode() ) : 4711 ) );
imageRenderer.distortImage();
}
String fileName = uniqueId + "." + imageRenderer.getImageFormatName(); // default is png
// write the result
assetUrl = writeRenderedImage( imageRenderer, fileName );
}
catch ( Exception e )
{
logger.error( "An error occurred trying to getRenderedTextUrl(), text = " + text + " :" + e.getMessage(), e );
}
return assetUrl;
}
private String writeRenderedImage( AdvancedImageRenderer imageRenderer, String fileName ) throws Exception
{
// write the result
int i = 0;
String filePath = PropertyHelper.getProperty("digitalAssetPath");
File imageFile = new File( filePath, fileName );
if ( !imageFile.exists() )
{
imageRenderer.writeImage( imageFile );
}
String urlBase = PropertyHelper.getProperty("urlBase");
String url = urlBase + "digitalAssets/" + fileName;
return url;
}
private void setAttribute( String key, Object value )
{
if ( renderAttributes == null )
{
renderAttributes = new HashMap();
}
if ( key != null && value != null )
{
renderAttributes.put( key, value.toString() );
}
}
protected void produceResult(Object value) throws JspTagException
{
if(id == null)
{
write((value == null) ? "" : value.toString());
}
else
{
setResultAttribute(value);
}
}
protected void write(final String text) throws JspTagException
{
try
{
pageContext.getOut().write(text);
}
catch(IOException e)
{
e.printStackTrace();
throw new JspTagException("IO error: " + e.getMessage());
}
}
protected void setResultAttribute(Object value)
{
if(value == null)
{
pageContext.removeAttribute(id);
}
else
{
pageContext.setAttribute(id, value);
}
}
public static void cleanOldFiles()
{
int i = 0;
String filePath = PropertyHelper.getProperty("digitalAssetPath");
logger.info("Cleaning files...");
File folder = new File(filePath);
File[] files = folder.listFiles();
logger.info("files:" + files.length);
for(int j=0; j<files.length; j++)
{
File file = files[j];
if(file.getName().startsWith("igcaptcha"))
{
logger.info("file.getName():" + file.getName() + " - " + (System.currentTimeMillis() - file.lastModified()));
if(System.currentTimeMillis() - file.lastModified() > 60000)
{
logger.info("Deleting:" + file.getName());
file.delete();
}
}
}
}
/**
* Sets the number of characters in the random string. Value must be greater
* than 0, otherwise default value (5) is used.
* @param numberOfCharacters the number of characters
*/
public void setNumberOfCharacters( int numberOfCharacters )
{
if( numberOfCharacters > 0 )
{
this.numberOfCharacters = numberOfCharacters;
}
}
/**
* Sets the variable name by which to store the captcha text in.
* @param numberOfCharacters the number of characters
*/
public void setTextVariableName( String textVariableName )
{
this.textVariableName = textVariableName;
}
/**
* Creates a char[] of random characters and numbers a-z,A-Z,0-9.
* The number of characters can be set by an attribute. Default is 5.
* @return a char[] of random characters and numbers
*/
private char[] createRandomCharacters()
{
Random r = new Random();
StringBuffer sb = new StringBuffer();
char[] buf = new char[numberOfCharacters];
for (int i = 0; i < buf.length; i++)
{
buf[i] = allowedCharacters.charAt(r.nextInt(allowedCharacters.length()));
}
return buf;
}
/**
* Spaces characters for easier reading
* @param characters the characters to space
* @return a <code>String</code> of spaced characters
*/
private String spaceCharacters( char[] characters )
{
StringBuffer sb = new StringBuffer(characters.length*2);
for( int i = 0; i <characters.length; i++ )
{
sb.append( characters[i] );
sb.append( " " );
}
return sb.toString();
}
public void setTwirlAngle(String twirlAspect) throws JspException
{
this.setAttribute("twirlAspect", ((Float)evaluate("gapcha", "twirlAspect", twirlAspect, Float.class)).floatValue());
}
public void setMarbleXScale(String marbleXScale) throws JspException
{
this.setAttribute("marbleXScale", ((Float)evaluate("gapcha", "marbleXScale", marbleXScale, Float.class)).floatValue());
}
public void setMarbleYScale(String marbleYScale) throws JspException
{
this.setAttribute("marbleYScale", ((Float)evaluate("gapcha", "marbleYScale", marbleYScale, Float.class)).floatValue());
}
public void setMarbleTurbulence(String marbleTurbulence) throws JspException
{
this.setAttribute("marbleTurbulence", ((Float)evaluate("gapcha", "marbleTurbulence", marbleTurbulence, Float.class)).floatValue());
}
public void setMarbleAmount(String marbleAmount) throws JspException
{
this.setAttribute("marbleAmount", ((Float)evaluate("gapcha", "marbleAmount", marbleAmount, Float.class)).floatValue());
}
public void setAllowedCharacters(String allowedCharacters) throws JspException
{
this.allowedCharacters = evaluateString("gapcha", "allowedCharacters", allowedCharacters);
}
public void setFontName( String fontName ) throws JspException
{
this.setAttribute( "fontName", evaluateString( "textRender", "fontName", fontName ) );
}
public void setFontSize( String fontSize ) throws JspException
{
this.setAttribute( "fontSize", evaluateInteger( "textRender", "fontSize", fontSize ) );
}
public void setFontStyle( String fontStyle ) throws JspException
{
this.setAttribute( "fontStyle", evaluateInteger( "textRender", "fontStyle", fontStyle ) );
}
public void setFgColor( String fgColor ) throws JspException
{
this.setAttribute( "fgColor", evaluateString( "textRender", "fgColor", fgColor ) );
}
public void setBgColor( String bgColor ) throws JspException
{
this.setAttribute( "bgColor", evaluateString( "textRender", "bgColor", bgColor ) );
}
public void setImageType( String imageType ) throws JspException
{
this.setAttribute( "imageType", evaluateInteger( "textRender", "imageType", imageType ) );
}
public void setMaxRows( String maxRows ) throws JspException
{
this.setAttribute( "maxRows", evaluateInteger( "textRender", "maxRows", maxRows ) );
}
public void setPad( String pad ) throws JspException
{
this.setAttribute( "pad", evaluateInteger( "textRender", "pad", pad ) );
}
public void setPadBottom( String padBottom ) throws JspException
{
this.setAttribute( "padBottom", evaluateInteger( "textRender", "padBottom", padBottom ) );
}
public void setPadLeft( String padLeft ) throws JspException
{
this.setAttribute( "padLeft", evaluateInteger( "textRender", "padLeft", padLeft ) );
}
public void setPadRight( String padRight ) throws JspException
{
this.setAttribute( "padRight", evaluateInteger( "textRender", "padRight", padRight ) );
}
public void setPadTop( String padTop ) throws JspException
{
this.setAttribute( "padTop", evaluateInteger( "textRender", "padTop", padTop ) );
}
public void setRenderWidth( String renderWidth ) throws JspException
{
this.setAttribute( "renderWidth", evaluateInteger( "textRender", "renderWidth", renderWidth ) );
}
public void setTileBackgroundImage( String tileBackgroundImage ) throws JspException
{
this.setAttribute( "tileBackgroundImage", evaluateInteger( "textRender", "tileBackgroundImage",
tileBackgroundImage ) );
}
public void setTrimEdges( String trimEdges ) throws JspException
{
this.setAttribute( "trimEdges", evaluateInteger( "textRender", "trimEdges", trimEdges ) );
}
}