1 /*
   2  * Copyright 1995-2008 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package java.util.zip;
  27 
  28 import java.io.Closeable;
  29 import java.io.InputStream;
  30 import java.io.IOException;
  31 import java.io.EOFException;
  32 import java.io.File;
  33 import java.nio.charset.Charset;
  34 import java.util.Vector;
  35 import java.util.Enumeration;
  36 import java.util.Set;
  37 import java.util.HashSet;
  38 import java.util.NoSuchElementException;
  39 import java.security.AccessController;
  40 import sun.security.action.GetPropertyAction;
  41 import static java.util.zip.ZipConstants64.*;
  42 
  43 /**
  44  * This class is used to read entries from a zip file.
  45  *
  46  * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
  47  * or method in this class will cause a {@link NullPointerException} to be
  48  * thrown.
  49  *
  50  * @author      David Connelly
  51  */
  52 public
  53 class ZipFile implements ZipConstants, Closeable {
  54     private long jzfile;  // address of jzfile data
  55     private String name;  // zip file name
  56     private int total;    // total number of entries
  57     private boolean closeRequested;
  58 
  59     private static final int STORED = ZipEntry.STORED;
  60     private static final int DEFLATED = ZipEntry.DEFLATED;
  61 
  62     /**
  63      * Mode flag to open a zip file for reading.
  64      */
  65     public static final int OPEN_READ = 0x1;
  66 
  67     /**
  68      * Mode flag to open a zip file and mark it for deletion.  The file will be
  69      * deleted some time between the moment that it is opened and the moment
  70      * that it is closed, but its contents will remain accessible via the
  71      * <tt>ZipFile</tt> object until either the close method is invoked or the
  72      * virtual machine exits.
  73      */
  74     public static final int OPEN_DELETE = 0x4;
  75 
  76     static {
  77         /* Zip library is loaded from System.initializeSystemClass */
  78         initIDs();
  79     }
  80 
  81     private static native void initIDs();
  82 
  83     private static final boolean usemmap;
  84 
  85     static {
  86         // A system prpperty to disable mmap use to avoid vm crash when
  87         // in-use zip file is accidently overwritten by others.
  88         String prop = AccessController.doPrivileged(
  89             new GetPropertyAction("sun.zip.disableMemoryMapping"));
  90         usemmap = (prop == null ||
  91                    !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); 
  92     }
  93 
  94     /**
  95      * Opens a zip file for reading.
  96      *
  97      * <p>First, if there is a security manager, its <code>checkRead</code>
  98      * method is called with the <code>name</code> argument as its argument
  99      * to ensure the read is allowed.
 100      *
 101      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
 102      * decode the entry names and comments.
 103      *
 104      * @param name the name of the zip file
 105      * @throws ZipException if a ZIP format error has occurred
 106      * @throws IOException if an I/O error has occurred
 107      * @throws SecurityException if a security manager exists and its
 108      *         <code>checkRead</code> method doesn't allow read access to the file.
 109      *
 110      * @see SecurityManager#checkRead(java.lang.String)
 111      */
 112     public ZipFile(String name) throws IOException {
 113         this(new File(name), OPEN_READ);
 114     }
 115 
 116     /**
 117      * Opens a new <code>ZipFile</code> to read from the specified
 118      * <code>File</code> object in the specified mode.  The mode argument
 119      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
 120      *
 121      * <p>First, if there is a security manager, its <code>checkRead</code>
 122      * method is called with the <code>name</code> argument as its argument to
 123      * ensure the read is allowed.
 124      *
 125      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
 126      * decode the entry names and comments
 127      *
 128      * @param file the ZIP file to be opened for reading
 129      * @param mode the mode in which the file is to be opened
 130      * @throws ZipException if a ZIP format error has occurred
 131      * @throws IOException if an I/O error has occurred
 132      * @throws SecurityException if a security manager exists and
 133      *         its <code>checkRead</code> method
 134      *         doesn't allow read access to the file,
 135      *         or its <code>checkDelete</code> method doesn't allow deleting
 136      *         the file when the <tt>OPEN_DELETE</tt> flag is set.
 137      * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
 138      * @see SecurityManager#checkRead(java.lang.String)
 139      * @since 1.3
 140      */
 141     public ZipFile(File file, int mode) throws IOException {
 142         this(file, mode, Charset.forName("UTF-8"));
 143     }
 144 
 145     /**
 146      * Opens a ZIP file for reading given the specified File object.
 147      *
 148      * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
 149      * decode the entry names and comments.
 150      *
 151      * @param file the ZIP file to be opened for reading
 152      * @throws ZipException if a ZIP format error has occurred
 153      * @throws IOException if an I/O error has occurred
 154      */
 155     public ZipFile(File file) throws ZipException, IOException {
 156         this(file, OPEN_READ);
 157     }
 158 
 159     private ZipCoder zc;
 160 
 161     /**
 162      * Opens a new <code>ZipFile</code> to read from the specified
 163      * <code>File</code> object in the specified mode.  The mode argument
 164      * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
 165      *
 166      * <p>First, if there is a security manager, its <code>checkRead</code>
 167      * method is called with the <code>name</code> argument as its argument to
 168      * ensure the read is allowed.
 169      *
 170      * @param file the ZIP file to be opened for reading
 171      * @param mode the mode in which the file is to be opened
 172      * @param charset
 173      *        the {@linkplain java.nio.charset.Charset charset} to
 174      *        be used to decode the ZIP entry name and comment that are not
 175      *        encoded by using UTF-8 encoding (indicated by entry's general
 176      *        purpose flag).
 177      *
 178      * @throws ZipException if a ZIP format error has occurred
 179      * @throws IOException if an I/O error has occurred
 180      *
 181      * @throws SecurityException
 182      *         if a security manager exists and its <code>checkRead</code>
 183      *         method doesn't allow read access to the file,or its
 184      *         <code>checkDelete</code> method doesn't allow deleting the
 185      *         file when the <tt>OPEN_DELETE</tt> flag is set
 186      *
 187      * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
 188      *
 189      * @see SecurityManager#checkRead(java.lang.String)
 190      *
 191      * @since 1.7
 192      */
 193     public ZipFile(File file, int mode, Charset charset) throws IOException
 194     {
 195         if (((mode & OPEN_READ) == 0) ||
 196             ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
 197             throw new IllegalArgumentException("Illegal mode: 0x"+
 198                                                Integer.toHexString(mode));
 199         }
 200         String name = file.getPath();
 201         SecurityManager sm = System.getSecurityManager();
 202         if (sm != null) {
 203             sm.checkRead(name);
 204             if ((mode & OPEN_DELETE) != 0) {
 205                 sm.checkDelete(name);
 206             }
 207         }
 208         if (charset == null)
 209             throw new NullPointerException("charset is null");
 210         this.zc = ZipCoder.get(charset);
 211         long t0 = System.nanoTime();
 212         jzfile = open(name, mode, file.lastModified(), usemmap);
 213         sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
 214         sun.misc.PerfCounter.getZipFileCount().increment();
 215         this.name = name;
 216         this.total = getTotal(jzfile);
 217     }
 218 
 219     /**
 220      * Opens a zip file for reading.
 221      *
 222      * <p>First, if there is a security manager, its <code>checkRead</code>
 223      * method is called with the <code>name</code> argument as its argument
 224      * to ensure the read is allowed.
 225      *
 226      * @param name the name of the zip file
 227      * @param charset
 228      *        the {@linkplain java.nio.charset.Charset charset} to
 229      *        be used to decode the ZIP entry name and comment that are not
 230      *        encoded by using UTF-8 encoding (indicated by entry's general
 231      *        purpose flag).
 232      *
 233      * @throws ZipException if a ZIP format error has occurred
 234      * @throws IOException if an I/O error has occurred
 235      * @throws SecurityException
 236      *         if a security manager exists and its <code>checkRead</code>
 237      *         method doesn't allow read access to the file
 238      *
 239      * @see SecurityManager#checkRead(java.lang.String)
 240      *
 241      * @since 1.7
 242      */
 243     public ZipFile(String name, Charset charset) throws IOException
 244     {
 245         this(new File(name), OPEN_READ, charset);
 246     }
 247 
 248     /**
 249      * Opens a ZIP file for reading given the specified File object.
 250      * @param file the ZIP file to be opened for reading
 251      * @param charset
 252      *        The {@linkplain java.nio.charset.Charset charset} to be
 253      *        used to decode the ZIP entry name and comment (ignored if
 254      *        the <a href="package-summary.html#lang_encoding"> language
 255      *        encoding bit</a> of the ZIP entry's general purpose bit
 256      *        flag is set).
 257      *
 258      * @throws ZipException if a ZIP format error has occurred
 259      * @throws IOException if an I/O error has occurred
 260      *
 261      * @since 1.7
 262      */
 263     public ZipFile(File file, Charset charset) throws IOException
 264     {
 265         this(file, OPEN_READ, charset);
 266     }
 267 
 268     /**
 269      * Returns the zip file comment, or null if none.
 270      *
 271      * @return the comment string for the zip file, or null if none
 272      *
 273      * @throws IllegalStateException if the zip file has been closed
 274      *
 275      * Since 1.7
 276      */
 277     public String getComment() {
 278         synchronized (this) {
 279             ensureOpen();
 280             byte[] bcomm = getCommentBytes(jzfile);
 281             if (bcomm == null)
 282                 return null;
 283             return zc.toString(bcomm, bcomm.length);
 284         }
 285     }
 286 
 287     /**
 288      * Returns the zip file entry for the specified name, or null
 289      * if not found.
 290      *
 291      * @param name the name of the entry
 292      * @return the zip file entry, or null if not found
 293      * @throws IllegalStateException if the zip file has been closed
 294      */
 295     public ZipEntry getEntry(String name) {
 296         if (name == null) {
 297             throw new NullPointerException("name");
 298         }
 299         long jzentry = 0;
 300         synchronized (this) {
 301             ensureOpen();
 302             jzentry = getEntry(jzfile, zc.getBytes(name), true);
 303             if (jzentry != 0) {
 304                 ZipEntry ze = getZipEntry(name, jzentry);
 305                 freeEntry(jzfile, jzentry);
 306                 return ze;
 307             }
 308         }
 309         return null;
 310     }
 311 
 312     private static native long getEntry(long jzfile, byte[] name,
 313                                         boolean addSlash);
 314 
 315     // freeEntry releases the C jzentry struct.
 316     private static native void freeEntry(long jzfile, long jzentry);
 317 
 318     // the outstanding inputstreams that need to be closed.
 319     private Set<ZipFileInputStream> streams = new HashSet<ZipFileInputStream>();
 320 
 321     /**
 322      * Returns an input stream for reading the contents of the specified
 323      * zip file entry.
 324      *
 325      * <p> Closing this ZIP file will, in turn, close all input
 326      * streams that have been returned by invocations of this method.
 327      *
 328      * @param entry the zip file entry
 329      * @return the input stream for reading the contents of the specified
 330      * zip file entry.
 331      * @throws ZipException if a ZIP format error has occurred
 332      * @throws IOException if an I/O error has occurred
 333      * @throws IllegalStateException if the zip file has been closed
 334      */
 335     public InputStream getInputStream(ZipEntry entry) throws IOException {
 336         if (entry == null) {
 337             throw new NullPointerException("entry");
 338         }
 339         long jzentry = 0;
 340         ZipFileInputStream in = null;
 341         synchronized (this) {
 342             ensureOpen();
 343             if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
 344                 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
 345             } else {
 346                 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
 347             }
 348             if (jzentry == 0) {
 349                 return null;
 350             }
 351             in = new ZipFileInputStream(jzentry);
 352             streams.add(in);
 353         }
 354         final ZipFileInputStream zfin = in;
 355         switch (getEntryMethod(jzentry)) {
 356         case STORED:
 357             return zfin;
 358         case DEFLATED:
 359             // MORE: Compute good size for inflater stream:
 360             long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
 361             if (size > 65536) size = 8192;
 362             if (size <= 0) size = 4096;
 363             return new InflaterInputStream(zfin, getInflater(), (int)size) {
 364                 private boolean isClosed = false;
 365 
 366                 public void close() throws IOException {
 367                     if (!isClosed) {
 368                         releaseInflater(inf);
 369                         this.in.close();
 370                         isClosed = true;
 371                     }
 372                 }
 373                 // Override fill() method to provide an extra "dummy" byte
 374                 // at the end of the input stream. This is required when
 375                 // using the "nowrap" Inflater option.
 376                 protected void fill() throws IOException {
 377                     if (eof) {
 378                         throw new EOFException(
 379                             "Unexpected end of ZLIB input stream");
 380                     }
 381                     len = this.in.read(buf, 0, buf.length);
 382                     if (len == -1) {
 383                         buf[0] = 0;
 384                         len = 1;
 385                         eof = true;
 386                     }
 387                     inf.setInput(buf, 0, len);
 388                 }
 389                 private boolean eof;
 390 
 391                 public int available() throws IOException {
 392                     if (isClosed)
 393                         return 0;
 394                     long avail = zfin.size() - inf.getBytesWritten();
 395                     return avail > (long) Integer.MAX_VALUE ?
 396                         Integer.MAX_VALUE : (int) avail;
 397                 }
 398             };
 399         default:
 400             throw new ZipException("invalid compression method");
 401         }
 402     }
 403 
 404     /*
 405      * Gets an inflater from the list of available inflaters or allocates
 406      * a new one.
 407      */
 408     private Inflater getInflater() {
 409         synchronized (inflaters) {
 410             int size = inflaters.size();
 411             if (size > 0) {
 412                 Inflater inf = (Inflater)inflaters.remove(size - 1);
 413                 return inf;
 414             } else {
 415                 return new Inflater(true);
 416             }
 417         }
 418     }
 419 
 420     /*
 421      * Releases the specified inflater to the list of available inflaters.
 422      */
 423     private void releaseInflater(Inflater inf) {
 424         synchronized (inflaters) {
 425             inf.reset();
 426             inflaters.add(inf);
 427         }
 428     }
 429 
 430     // List of available Inflater objects for decompression
 431     private Vector inflaters = new Vector();
 432 
 433     /**
 434      * Returns the path name of the ZIP file.
 435      * @return the path name of the ZIP file
 436      */
 437     public String getName() {
 438         return name;
 439     }
 440 
 441     /**
 442      * Returns an enumeration of the ZIP file entries.
 443      * @return an enumeration of the ZIP file entries
 444      * @throws IllegalStateException if the zip file has been closed
 445      */
 446     public Enumeration<? extends ZipEntry> entries() {
 447         ensureOpen();
 448         return new Enumeration<ZipEntry>() {
 449                 private int i = 0;
 450                 public boolean hasMoreElements() {
 451                     synchronized (ZipFile.this) {
 452                         ensureOpen();
 453                         return i < total;
 454                     }
 455                 }
 456                 public ZipEntry nextElement() throws NoSuchElementException {
 457                     synchronized (ZipFile.this) {
 458                         ensureOpen();
 459                         if (i >= total) {
 460                             throw new NoSuchElementException();
 461                         }
 462                         long jzentry = getNextEntry(jzfile, i++);
 463                         if (jzentry == 0) {
 464                             String message;
 465                             if (closeRequested) {
 466                                 message = "ZipFile concurrently closed";
 467                             } else {
 468                                 message = getZipMessage(ZipFile.this.jzfile);
 469                             }
 470                             throw new ZipError("jzentry == 0" +
 471                                                ",\n jzfile = " + ZipFile.this.jzfile +
 472                                                ",\n total = " + ZipFile.this.total +
 473                                                ",\n name = " + ZipFile.this.name +
 474                                                ",\n i = " + i +
 475                                                ",\n message = " + message
 476                                 );
 477                         }
 478                         ZipEntry ze = getZipEntry(null, jzentry);
 479                         freeEntry(jzfile, jzentry);
 480                         return ze;
 481                     }
 482                 }
 483             };
 484     }
 485 
 486     private ZipEntry getZipEntry(String name, long jzentry) {
 487         ZipEntry e = new ZipEntry();
 488         e.flag = getEntryFlag(jzentry);  // get the flag first
 489         if (name != null) {
 490             e.name = name;
 491         } else {
 492             byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME);
 493             if (!zc.isUTF8() && (e.flag & EFS) != 0) {
 494                 e.name = zc.toStringUTF8(bname, bname.length);
 495             } else {
 496                 e.name = zc.toString(bname, bname.length);
 497             }
 498         }
 499         e.time = getEntryTime(jzentry);
 500         e.crc = getEntryCrc(jzentry);
 501         e.size = getEntrySize(jzentry);
 502         e. csize = getEntryCSize(jzentry);
 503         e.method = getEntryMethod(jzentry);
 504         e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA);
 505         byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
 506         if (bcomm == null) {
 507             e.comment = null;
 508         } else {
 509             if (!zc.isUTF8() && (e.flag & EFS) != 0) {
 510                 e.comment = zc.toStringUTF8(bcomm, bcomm.length);
 511             } else {
 512                 e.comment = zc.toString(bcomm, bcomm.length);
 513             }
 514         }
 515         return e;
 516     }
 517 
 518     private static native long getNextEntry(long jzfile, int i);
 519 
 520     /**
 521      * Returns the number of entries in the ZIP file.
 522      * @return the number of entries in the ZIP file
 523      * @throws IllegalStateException if the zip file has been closed
 524      */
 525     public int size() {
 526         ensureOpen();
 527         return total;
 528     }
 529 
 530     /**
 531      * Closes the ZIP file.
 532      * <p> Closing this ZIP file will close all of the input streams
 533      * previously returned by invocations of the {@link #getInputStream
 534      * getInputStream} method.
 535      *
 536      * @throws IOException if an I/O error has occurred
 537      */
 538     public void close() throws IOException {
 539         synchronized (this) {
 540             closeRequested = true;
 541 
 542             if (streams.size() !=0) {
 543                 Set<ZipFileInputStream> copy = streams;
 544                 streams = new HashSet<ZipFileInputStream>();
 545                 for (ZipFileInputStream is: copy)
 546                     is.close();
 547             }
 548 
 549             if (jzfile != 0) {
 550                 // Close the zip file
 551                 long zf = this.jzfile;
 552                 jzfile = 0;
 553 
 554                 close(zf);
 555 
 556                 // Release inflaters
 557                 synchronized (inflaters) {
 558                     int size = inflaters.size();
 559                     for (int i = 0; i < size; i++) {
 560                         Inflater inf = (Inflater)inflaters.get(i);
 561                         inf.end();
 562                     }
 563                 }
 564             }
 565         }
 566     }
 567 
 568 
 569     /**
 570      * Ensures that the <code>close</code> method of this ZIP file is
 571      * called when there are no more references to it.
 572      *
 573      * <p>
 574      * Since the time when GC would invoke this method is undetermined,
 575      * it is strongly recommended that applications invoke the <code>close</code>
 576      * method as soon they have finished accessing this <code>ZipFile</code>.
 577      * This will prevent holding up system resources for an undetermined
 578      * length of time.
 579      *
 580      * @throws IOException if an I/O error has occurred
 581      * @see    java.util.zip.ZipFile#close()
 582      */
 583     protected void finalize() throws IOException {
 584         close();
 585     }
 586 
 587     private static native void close(long jzfile);
 588 
 589     private void ensureOpen() {
 590         if (closeRequested) {
 591             throw new IllegalStateException("zip file closed");
 592         }
 593 
 594         if (jzfile == 0) {
 595             throw new IllegalStateException("The object is not initialized.");
 596         }
 597     }
 598 
 599     private void ensureOpenOrZipException() throws IOException {
 600         if (closeRequested) {
 601             throw new ZipException("ZipFile closed");
 602         }
 603     }
 604 
 605     /*
 606      * Inner class implementing the input stream used to read a
 607      * (possibly compressed) zip file entry.
 608      */
 609    private class ZipFileInputStream extends InputStream {
 610         protected long jzentry; // address of jzentry data
 611         private   long pos;     // current position within entry data
 612         protected long rem;     // number of remaining bytes within entry
 613         protected long size;    // uncompressed size of this entry
 614 
 615         ZipFileInputStream(long jzentry) {
 616             pos = 0;
 617             rem = getEntryCSize(jzentry);
 618             size = getEntrySize(jzentry);
 619             this.jzentry = jzentry;
 620         }
 621 
 622         public int read(byte b[], int off, int len) throws IOException {
 623             if (rem == 0) {
 624                 return -1;
 625             }
 626             if (len <= 0) {
 627                 return 0;
 628             }
 629             if (len > rem) {
 630                 len = (int) rem;
 631             }
 632             synchronized (ZipFile.this) {
 633                 ensureOpenOrZipException();
 634 
 635                 len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b,
 636                                    off, len);
 637             }
 638             if (len > 0) {
 639                 pos += len;
 640                 rem -= len;
 641             }
 642             if (rem == 0) {
 643                 close();
 644             }
 645             return len;
 646         }
 647 
 648         public int read() throws IOException {
 649             byte[] b = new byte[1];
 650             if (read(b, 0, 1) == 1) {
 651                 return b[0] & 0xff;
 652             } else {
 653                 return -1;
 654             }
 655         }
 656 
 657         public long skip(long n) {
 658             if (n > rem)
 659                 n = rem;
 660             pos += n;
 661             rem -= n;
 662             if (rem == 0) {
 663                 close();
 664             }
 665             return n;
 666         }
 667 
 668         public int available() {
 669             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
 670         }
 671 
 672         public long size() {
 673             return size;
 674         }
 675 
 676         public void close() {
 677             rem = 0;
 678             synchronized (ZipFile.this) {
 679                 if (jzentry != 0 && ZipFile.this.jzfile != 0) {
 680                     freeEntry(ZipFile.this.jzfile, jzentry);
 681                     jzentry = 0;
 682                 }
 683                 streams.remove(this);
 684             }
 685         }
 686     }
 687 
 688 
 689     private static native long open(String name, int mode, long lastModified,
 690                                     boolean usemmap) throws IOException;
 691     private static native int getTotal(long jzfile);
 692     private static native int read(long jzfile, long jzentry,
 693                                    long pos, byte[] b, int off, int len);
 694 
 695     // access to the native zentry object
 696     private static native long getEntryTime(long jzentry);
 697     private static native long getEntryCrc(long jzentry);
 698     private static native long getEntryCSize(long jzentry);
 699     private static native long getEntrySize(long jzentry);
 700     private static native int getEntryMethod(long jzentry);
 701     private static native int getEntryFlag(long jzentry);
 702     private static native byte[] getCommentBytes(long jzfile);
 703 
 704     private static final int JZENTRY_NAME = 0;
 705     private static final int JZENTRY_EXTRA = 1;
 706     private static final int JZENTRY_COMMENT = 2;
 707     private static native byte[] getEntryBytes(long jzentry, int type);
 708 
 709     private static native String getZipMessage(long jzfile);
 710 }