ZipEntry.java

package sk.iway.iwcm.system.zip;

/**
 *  ZipEntry.java
 *
 *@Title        webjet4
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2005
 *@author       $Author: jeeff $
 *@version      $Revision: 1.3 $
 *@created      Date: 14.3.2005 13:31:29
 *@modified     $Date: 2007/09/07 13:39:29 $
 */
/*
 * @(#)ZipEntry.java	1.35 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
import java.util.Date;

/**
 * This class is used to represent a ZIP file entry.
 *
 * @version 1.35, 01/23/03
 * @author David Connelly
 */
@SuppressWarnings("deprecation")
public class ZipEntry implements ZipConstants, Cloneable
{
	String name; // entry name
	long time = -1; // modification time (in DOS time)
	long crc = -1; // crc-32 of entry data
	long size = -1; // uncompressed size of entry data
	long csize = -1; // compressed size of entry data
	int method = -1; // compression method
	byte[] extra; // optional extra field data for entry
	String comment; // optional comment string for entry
	// The following flags are used only by Zip{Input,Output}Stream
	int flag; // bit flags
	int version; // version needed to extract
	long offset; // offset of loc header
	/**
	 * Compression method for uncompressed entries.
	 */
	public static final int STORED = 0;
	/**
	 * Compression method for compressed (deflated) entries.
	 */
	public static final int DEFLATED = 8;

	/**
	 * Creates a new zip entry with the specified name.
	 *
	 * @param name
	 *           the entry name
	 * @exception NullPointerException
	 *               if the entry name is null
	 * @exception IllegalArgumentException
	 *               if the entry name is longer than 0xFFFF bytes
	 */
	public ZipEntry(String name)
	{
		if (name == null)
		{
			throw new NullPointerException();
		}
		if (name.length() > 0xFFFF)
		{
			throw new IllegalArgumentException("entry name too long");
		}
		//WebJET
		this.name = name.trim();
	}

	/**
	 * Creates a new zip entry with fields taken from the specified zip entry.
	 *
	 * @param e
	 *           a zip Entry object
	 */
	public ZipEntry(ZipEntry e)
	{
		name = e.name;
		time = e.time;
		crc = e.crc;
		size = e.size;
		csize = e.csize;
		method = e.method;
		extra = e.extra;
		comment = e.comment;
	}

	/*
	 * Creates a new zip entry for the given name with fields initialized from
	 * the specified jzentry data.
	 */
	ZipEntry(String name, long jzentry)
	{
		this.name = name;
		initFields(jzentry);
	}

	private native void initFields(long jzentry);

	/*
	 * Creates a new zip entry with fields initialized from the specified jzentry
	 * data.
	 */
	ZipEntry(long jzentry)
	{
		initFields(jzentry);
	}

	/**
	 * Returns the name of the entry.
	 *
	 * @return the name of the entry
	 */
	public String getName()
	{
		//WebJET - [#37426]VYSLEDKY Z PENTRACNYCH TESTOV - ochrana pred zip evil
		String nameFixed = name;
		if (nameFixed != null && nameFixed.contains(".."))
		{
			//cesta je vo formate ../../../test.jsp
			int i = nameFixed.lastIndexOf("/");
			if (i == -1) i = nameFixed.lastIndexOf("\\");
			if (i > 0)
			{
				nameFixed = nameFixed.substring(i+1);
			}
		}

		return nameFixed;
	}

	/**
	 * Sets the modification time of the entry.
	 *
	 * @param time
	 *           the entry modification time in number of milliseconds since the
	 *           epoch
	 * @see #getTime()
	 */
	public void setTime(long time)
	{
		this.time = javaToDosTime(time);
	}

	/**
	 * Returns the modification time of the entry, or -1 if not specified.
	 *
	 * @return the modification time of the entry, or -1 if not specified
	 * @see #setTime(long)
	 */
	public long getTime()
	{
		return time != -1 ? dosToJavaTime(time) : -1;
	}

	/**
	 * Sets the uncompressed size of the entry data.
	 *
	 * @param size
	 *           the uncompressed size in bytes
	 * @exception IllegalArgumentException
	 *               if the specified size is less than 0 or greater than
	 *               0xFFFFFFFF bytes
	 * @see #getSize()
	 */
	public void setSize(long size)
	{
		if (size < 0 || size > 0xFFFFFFFFL)
		{
			throw new IllegalArgumentException("invalid entry size");
		}
		this.size = size;
	}

	/**
	 * Returns the uncompressed size of the entry data, or -1 if not known.
	 *
	 * @return the uncompressed size of the entry data, or -1 if not known
	 * @see #setSize(long)
	 */
	public long getSize()
	{
		return size;
	}

	/**
	 * Returns the size of the compressed entry data, or -1 if not known. In the
	 * case of a stored entry, the compressed size will be the same as the
	 * uncompressed size of the entry.
	 *
	 * @return the size of the compressed entry data, or -1 if not known
	 * @see #setCompressedSize(long)
	 */
	public long getCompressedSize()
	{
		return csize;
	}

	/**
	 * Sets the size of the compressed entry data.
	 *
	 * @param csize
	 *           the compressed size to set to
	 * @see #getCompressedSize()
	 */
	public void setCompressedSize(long csize)
	{
		this.csize = csize;
	}

	/**
	 * Sets the CRC-32 checksum of the uncompressed entry data.
	 *
	 * @param crc
	 *           the CRC-32 value
	 * @exception IllegalArgumentException
	 *               if the specified CRC-32 value is less than 0 or greater than
	 *               0xFFFFFFFF
	 * @see #setCrc(long)
	 */
	public void setCrc(long crc)
	{
		if (crc < 0 || crc > 0xFFFFFFFFL)
		{
			throw new IllegalArgumentException("invalid entry crc-32");
		}
		this.crc = crc;
	}

	/**
	 * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if not
	 * known.
	 *
	 * @return the CRC-32 checksum of the uncompressed entry data, or -1 if not
	 *         known
	 * @see #getCrc()
	 */
	public long getCrc()
	{
		return crc;
	}

	/**
	 * Sets the compression method for the entry.
	 *
	 * @param method
	 *           the compression method, either STORED or DEFLATED
	 * @exception IllegalArgumentException
	 *               if the specified compression method is invalid
	 * @see #getMethod()
	 */
	public void setMethod(int method)
	{
		if (method != STORED && method != DEFLATED)
		{
			throw new IllegalArgumentException("invalid compression method");
		}
		this.method = method;
	}

	/**
	 * Returns the compression method of the entry, or -1 if not specified.
	 *
	 * @return the compression method of the entry, or -1 if not specified
	 * @see #setMethod(int)
	 */
	public int getMethod()
	{
		return method;
	}

	/**
	 * Sets the optional extra field data for the entry.
	 *
	 * @param extra
	 *           the extra field data bytes
	 * @exception IllegalArgumentException
	 *               if the length of the specified extra field data is greater
	 *               than 0xFFFF bytes
	 * @see #getExtra()
	 */
	public void setExtra(byte[] extra)
	{
		if (extra != null && extra.length > 0xFFFF)
		{
			throw new IllegalArgumentException("invalid extra field length");
		}
		this.extra = extra; 
	}

	/**
	 * Returns the extra field data for the entry, or null if none.
	 *
	 * @return the extra field data for the entry, or null if none
	 * @see #setExtra(byte[])
	 */
	public byte[] getExtra()
	{
		return extra;
	}

	/**
	 * Sets the optional comment string for the entry.
	 *
	 * @param comment
	 *           the comment string
	 * @exception IllegalArgumentException
	 *               if the length of the specified comment string is greater
	 *               than 0xFFFF bytes
	 * @see #getComment()
	 */
	public void setComment(String comment)
	{
		if (comment != null && comment.length() > 0xffff / 3 && getUTF8Length(comment) > 0xffff)
		{
			throw new IllegalArgumentException("invalid entry comment length");
		}
		this.comment = comment;
	}

	static int getUTF8Length(String s)
	{
		int count = 0;
		for (int i = 0; i < s.length(); i++)
		{
			char ch = s.charAt(i);
			if (ch <= 0x7f)
			{
				count++;
			}
			else if (ch <= 0x7ff)
			{
				count += 2;
			}
			else
			{
				count += 3;
			}
		}
		return count;
	}

	/**
	 * Returns the comment string for the entry, or null if none.
	 *
	 * @return the comment string for the entry, or null if none
	 * @see #setComment(String)
	 */
	public String getComment()
	{
		return comment;
	}

	/**
	 * Returns true if this is a directory entry. A directory entry is defined to
	 * be one whose name ends with a '/'.
	 *
	 * @return true if this is a directory entry
	 */
	public boolean isDirectory()
	{
		return name.endsWith("/");
	}

	/**
	 * Returns a string representation of the ZIP entry.
	 */
	@Override
	public String toString()
	{
		return getName();
	}

	/*
	 * Converts DOS time to Java time (number of milliseconds since epoch).
	 */	
	private static long dosToJavaTime(long dtime)
	{
		Date d = new Date((int) (((dtime >> 25) & 0x7f) + 80), (int) (((dtime >> 21) & 0x0f) - 1), (int) ((dtime >> 16) & 0x1f),
					(int) ((dtime >> 11) & 0x1f), (int) ((dtime >> 5) & 0x3f), (int) ((dtime << 1) & 0x3e));
		return d.getTime();
	}

	/*
	 * Converts Java time to DOS time.
	 */
	private static long javaToDosTime(long time)
	{
		Date d = new Date(time);
		int year = d.getYear() + 1900;
		if (year < 1980)
		{
			return (1 << 21) | (1 << 16);
		}
		return (year - 1980) << 25 | (d.getMonth() + 1) << 21 | d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5
					| d.getSeconds() >> 1;
	}

	/**
	 * Returns the hash code value for this entry.
	 */
	@Override
	public int hashCode()
	{
		return name.hashCode();
	}

	/**
	 * Returns a copy of this entry.
	 */
	@Override
	public Object clone()
	{
		try
		{
			ZipEntry e = (ZipEntry) super.clone();
			e.extra = (extra == null ? null : (byte[]) extra.clone());
			return e;
		}
		catch (CloneNotSupportedException e)
		{
			// This should never happen, since we are Cloneable
			throw new InternalError();
		}
	}
}