/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.client.filter; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import freenet.l10n.NodeL10n; import freenet.support.io.FileUtil; /** * @author kurmiashish * This class would verify whether the BMP header is valid or not Reference: http://www.fastgraph.com/help/bmp_header_format.html http://en.wikipedia.org/wiki/BMP_file_format */ public class BMPFilter implements ContentDataFilter { static final byte[] bmpHeaderwindows = { (byte)'B', (byte)'M'}; static final byte[] bmpHeaderos2bArray = { (byte)'B', (byte)'A'}; static final byte[] bmpHeaderos2cIcon = { (byte)'C', (byte)'I'}; static final byte[] bmpHeaderos2cPointer = { (byte)'C', (byte)'P'}; static final byte[] bmpHeaderos2Icon = { (byte)'I', (byte)'C'}; static final byte[] bmpHeaderos2Pointer = { (byte)'P', (byte)'T'}; private int unsignedByte(byte b) { if (b >= 0) return b; else return 256+b; } public int readInt(DataInputStream dis) throws IOException { int result; byte[] data = new byte[4]; result = dis.read(data); if (result < 0) // end of file reached throw new EOFException(); result = (unsignedByte(data[2]) << 16) | (unsignedByte(data[1]) << 8) | unsignedByte(data[0]); result|=(unsignedByte(data[3]) << 24); return result; } public int readShort(DataInputStream dis) throws IOException { int result = dis.read(); if (result < 0)// end of file reached throw new EOFException(); int r2 = dis.read(); if (r2 < 0)// end of file reached throw new EOFException(); return result | (r2*256); } @Override public void readFilter(InputStream input, OutputStream output, String charset, HashMap<String, String> otherParams, FilterCallback cb) throws DataFilterException, IOException { DataInputStream dis = new DataInputStream(input); dis.mark(54); byte[] StartWord = new byte[2]; dis.readFully(StartWord); if((!Arrays.equals(StartWord, bmpHeaderwindows)) && (!Arrays.equals(StartWord, bmpHeaderos2bArray)) && (!Arrays.equals(StartWord, bmpHeaderos2cIcon)) && (!Arrays.equals(StartWord, bmpHeaderos2cPointer)) && (!Arrays.equals(StartWord, bmpHeaderos2Icon)) && (!Arrays.equals(StartWord, bmpHeaderos2Pointer))) { //Checking the first word throwHeaderError(l10n("InvalidStartWordT"), l10n("InvalidStartWordD")); } int fileSize = readInt(dis); // read total file size byte[] skipbytes=new byte[4]; dis.readFully(skipbytes); int headerSize = readInt(dis); // read file header size or pixel offset if(headerSize<0) { throwHeaderError(l10n("InvalidOffsetT"), l10n("InvalidOffsetD")); } int size_bitmapinfoheader=readInt(dis); if(size_bitmapinfoheader!=40) { throwHeaderError(l10n("InvalidBitMapInfoHeaderSizeT"), l10n("InvalidBitMapInfoHeaderSizeD")); } int imageWidth = readInt(dis); // read width int imageHeight = readInt(dis); // read height if(imageWidth<0 || imageHeight<0) { throwHeaderError(l10n("InvalidDimensionT"), l10n("InvalidDimensionD")); } int no_plane=readShort(dis); if(no_plane!=1) { // No of planes should be 1 throwHeaderError(l10n("InvalidNoOfPlanesT"), l10n("InvalidNoOfPlanesD")); } int bitDepth = readShort(dis); // Bit depth should be 1,2,4,8,16 or 32. if(bitDepth!=1 && bitDepth!=2 && bitDepth!=4 && bitDepth!=8 && bitDepth!=16 && bitDepth!=24 && bitDepth!=32) { throwHeaderError(l10n("InvalidBitDepthT"), l10n("InvalidBitDepthD")); } int compression_type=readInt(dis); if( !(compression_type>=0 && compression_type<=3) ) { throwHeaderError(l10n("Invalid Compression type"), l10n("Compression type field is set to "+compression_type+" instead of 0-3")); } int imagedatasize=readInt(dis); if(fileSize!=headerSize+imagedatasize) { throwHeaderError(l10n("InvalidFileSizeT"), l10n("InvalidFileSizeD")); } int horizontal_resolution=readInt(dis); int vertical_resolution=readInt(dis); if(horizontal_resolution<0 || vertical_resolution<0) { throwHeaderError(l10n("InvalidResolutionT"), l10n("InvalidResolutionD")); } if(compression_type==0) { // Verifying the file size w.r.t. image dimensions(width and height), bitDepth with imagedatasize(including padding). int bitsPerLine = imageWidth * bitDepth; //Lines are padded to a 4 byte boundary if(bitsPerLine % 32 != 0) { bitsPerLine += 32 - bitsPerLine % 32; } int bytesPerLine = bitsPerLine / 8; int calculatedsize = bytesPerLine * imageHeight; if(calculatedsize!=imagedatasize) { throwHeaderError(l10n("InvalidImageDataSizeT"), l10n("InvalidImageDataSizeD" )); } } dis.reset(); FileUtil.copy(dis, output, -1); output.flush(); } private static String l10n(String key) { return NodeL10n.getBase().getString("BMPFilter."+key); } private void throwHeaderError(String shortReason, String reason) throws DataFilterException { // Throw an exception String message = l10n("notBMP"); if(reason != null) message += ' ' + reason; if(shortReason != null) message += " - (" + shortReason + ')'; throw new DataFilterException(shortReason, shortReason, message); } @Override public void writeFilter(InputStream input, OutputStream output, String charset, HashMap<String, String> otherParams, FilterCallback cb) throws DataFilterException, IOException { return; } }