package com.planet_ink.coffee_mud.WebMacros; /** * Copyright (c) 2006 by Jeff Haynie * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.UUID; import javax.imageio.ImageIO; import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine; import com.planet_ink.coffee_web.http.MIMEType; import com.planet_ink.coffee_web.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine.PlayerData; import com.planet_ink.coffee_mud.MOBS.interfaces.MOB; import com.planet_ink.coffee_mud.core.B64Encoder; import com.planet_ink.coffee_mud.core.CMLib; import com.planet_ink.coffee_mud.core.Log; import com.planet_ink.coffee_mud.core.Resources; import com.planet_ink.coffee_mud.core.collections.Pair; import com.planet_ink.coffee_mud.core.collections.SLinkedList; import com.planet_ink.coffee_mud.core.exceptions.HTTPServerException; /** * ImageVerification is a simple utility class for * creating an image verification PNG file that will * allow you to make sure that only a human can read * the alphanumeric values and enter them into a text * field during verification. * * Make sure that when you can getVerificationCode * you don't encode the value in the URL or inside the * HTML form - otherwise, this whole excerise is pointless * (dummy!). * * @author Jeff Haynie * Copyright (c) by Jeff Haynie. All Rights Reserved. */ public class ImageVerificationImage extends StdWebMacro { private String value; public static Object sync=new Object(); private static Random rand=new Random(); @Override public boolean isAWebPath() { return true; } @Override public boolean preferBinary() { return true; } public ImageVerificationImage () { } public static class ImgCacheEntry { public String key; public String value; public long createdTimeMillis=System.currentTimeMillis(); public String ip; } @SuppressWarnings("unchecked") public static SLinkedList<ImgCacheEntry> getVerifyCache() { SLinkedList<ImgCacheEntry> verSet=(SLinkedList<ImgCacheEntry>)Resources.getResource("SYSTEM_WEB_IMGVER_CACHE"); if(verSet==null) { verSet=new SLinkedList<ImgCacheEntry>(); Resources.submitResource("SYSTEM_WEB_IMGVER_CACHE", verSet); } try { while(verSet.size()>1000) verSet.removeFirst(); for(final Iterator<ImgCacheEntry> i=verSet.iterator();i.hasNext();) { final ImgCacheEntry I=i.next(); if((System.currentTimeMillis()-I.createdTimeMillis)>(20 * 60 * 60 * 1000)) i.remove(); } } catch(final Exception e) { } return verSet; } public String getFilename(final HTTPRequest httpReq, final String filename) { final String foundFilename=httpReq.getUrlParameter("FILENAME"); if((foundFilename!=null)&&(foundFilename.length()>0)) return foundFilename; return filename; } @Override public byte[] runBinaryMacro(final HTTPRequest httpReq, final String parm, final HTTPResponse httpResp) throws HTTPServerException { final ByteArrayOutputStream bout=new ByteArrayOutputStream(); try { synchronized(sync) { final boolean imageRequest=httpReq.isUrlParameter("IMAGE"); if(imageRequest) { final MIMEType mimeType = MIMEType.All.getMIMEType(getFilename(httpReq,"")); if(mimeType != null) httpResp.setHeader("Content-Type", mimeType.getType()); } final SLinkedList<ImgCacheEntry> cache = getVerifyCache(); String value=null; String key=null; final String hisIp=httpReq.getClientAddress().getHostAddress(); if(imageRequest) { for(final Iterator<ImageVerificationImage.ImgCacheEntry> p =cache.descendingIterator();p.hasNext();) { final ImageVerificationImage.ImgCacheEntry entry=p.next(); if(entry.ip.equalsIgnoreCase(hisIp)) { value=entry.value; key=entry.key; break; } } } final ImageVerificationImage img=new ImageVerificationImage(value,bout); if(key==null) key=Long.toHexString(Math.round(Math.abs(rand.nextDouble() * (Long.MAX_VALUE/2.0)))); if(value==null) { final ImgCacheEntry entry=new ImgCacheEntry(); entry.ip=hisIp; entry.key=key; entry.value=img.getVerificationValue(); cache.addLast(entry); } if(!imageRequest) { bout.reset(); bout.write(key.getBytes()); } httpReq.addFakeUrlParameter("IMGVERKEY", key); } } catch(final IOException ioe) { Log.errOut("ImgVerWM",ioe); } return bout.toByteArray(); } @Override public String runMacro(final HTTPRequest httpReq, final String parm, final HTTPResponse httpResp) throws HTTPServerException { return "[Unimplemented string method!]"; } public ImageVerificationImage (final String oldValue, final OutputStream out) throws IOException { this(34,120,oldValue,out); } public ImageVerificationImage (final int height, final int width, final String oldValue, final OutputStream out) throws IOException { final BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); final Random rand=new Random(System.currentTimeMillis()); final Graphics2D g = bimage.createGraphics(); // create a random color final Color color = new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)); // the background to the random color to fill the // background and make it darker g.setColor(color.darker()); g.fillRect(0, 0, width, height); // set the font g.setFont(new Font("arial",Font.BOLD,24)); // generate a random value if(oldValue!=null) this.value = oldValue; else this.value = UUID.randomUUID().toString().replace("-","").substring(0,5); final int w = (g.getFontMetrics()).stringWidth(value); final int d = (g.getFontMetrics()).getDescent(); final int a = (g.getFontMetrics()).getMaxAscent(); int x = 0, y =0; // randomly set the color and draw some straight lines through it for (int i = 0; i < height; i += 5) { g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255))); g.drawLine(x, y + i, width, y+i); } // reset x and y x=0; y=0; // randomly set the color of the lines and just draw think at an angle for (int i = 0; i < height; i += 5) { g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255))); g.drawLine(x, y + d - i, width + w, height + d - i); } // randomly set the color and make it really bright for more readability g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)).brighter().brighter()); // we need to position the text in the center of the box x = width/2 - w/2; y = height/2 + a/2 - 2; // affine transform is used to rock the text a bit final AffineTransform fontAT = new AffineTransform(); int xp = x-2; // walk through each character and rotate it randomly for (int c=0;c<value.length();c++) { // apply a random radian either left or right (left is half since it's too far back) final int rotate = rand.nextInt(20); fontAT.rotate(rand.nextBoolean() ? Math.toRadians(rotate) : -Math.toRadians(rotate/2)); final Font fx = new Font("arial", Font.BOLD, 24).deriveFont(fontAT); g.setFont(fx); final String ch = String.valueOf(value.charAt(c)); final int ht = rand.nextInt(3); // draw the string and move the y either up or down slightly g.drawString(ch, xp, y + (rand.nextBoolean()?-ht:ht)); // move our pointer xp+=g.getFontMetrics().stringWidth(ch) + 2; } // write out the PNG file ImageIO.write(bimage, "png", out); // make sure your clean up the graphics object g.dispose(); } /** * return the value to check for when the user enters it in. Make sure you * store this off in the session or something like a database and NOT in the * form of the webpage since the whole point of this exercise is to ensure that * only humans and not machines are entering the data. * * @return the value to enter */ public String getVerificationValue () { return this.value; } }