1 /*
   2  * Copyright (c) 2005, Oracle and/or its affiliates. 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @bug 4770745 6234507
  26  * @summary test a variety of zip file entries
  27  * @author Martin Buchholz
  28  */
  29 
  30 import java.util.*;
  31 import java.util.zip.*;
  32 import java.util.jar.*;
  33 import java.io.*;
  34 
  35 public class Assortment {
  36     static int passed = 0, failed = 0;
  37 
  38     static void fail(String msg) {
  39         failed++;
  40         new Exception(msg).printStackTrace();
  41     }
  42 
  43     static void unexpected(Throwable t) {
  44         failed++;
  45         t.printStackTrace();
  46     }
  47 
  48     static void check(boolean condition, String msg) {
  49         if (! condition)
  50             fail(msg);
  51     }
  52 
  53     static void check(boolean condition) {
  54         check(condition, "Something's wrong");
  55     }
  56 
  57     private static class Entry {
  58         private String name;
  59         private int    method;
  60         private byte[] data;
  61         private byte[] extra;
  62         private String comment;
  63 
  64         Entry(String name,
  65               int    method,
  66               byte[] data,
  67               byte[] extra,
  68               String comment) {
  69             this.name    = name;
  70             this.method  = method;
  71             this.data    = data;
  72             this.extra   = extra;
  73             this.comment = comment;
  74         }
  75 
  76         void write(ZipOutputStream s) throws Exception {
  77             ZipEntry e = new ZipEntry(name);
  78             CRC32 crc32 = new CRC32();
  79             e.setMethod(method);
  80             if (method == ZipEntry.STORED) {
  81                 e.setSize(data == null ? 0 : data.length);
  82                 crc32.reset();
  83                 if (data != null) crc32.update(data);
  84                 e.setCrc(crc32.getValue());
  85             } else {
  86                 e.setSize(0);
  87                 e.setCrc(0);
  88             }
  89             if (comment != null) e.setComment(comment);
  90             if (extra   != null) e.setExtra(extra);
  91             s.putNextEntry(e);
  92             if (data != null) s.write(data);
  93         }
  94 
  95         byte[] getData(ZipFile f, ZipEntry e) throws Exception {
  96             byte[] fdata = new byte[(int)e.getSize()];
  97             InputStream is = f.getInputStream(e);
  98             is.read(fdata);
  99             return fdata;
 100         }
 101 
 102         void verify(ZipFile f) throws Exception {
 103             ZipEntry e = f.getEntry(name);
 104             byte[] data  = (this.data == null) ? new byte[]{} : this.data;
 105             byte[] extra = (this.extra != null && this.extra.length == 0) ?
 106                 null : this.extra;
 107             check(name.equals(e.getName()));
 108             check(method == e.getMethod());
 109             check((((comment == null) || comment.equals(""))
 110                    && (e.getComment() == null))
 111                   || comment.equals(e.getComment()));
 112             check(Arrays.equals(extra, e.getExtra()));
 113             check(Arrays.equals(data, getData(f, e)));
 114             check(e.getSize() == data.length);
 115             check((method == ZipEntry.DEFLATED) ||
 116                   (e.getCompressedSize() == data.length));
 117         }
 118 
 119         void verify(JarInputStream jis) throws Exception {
 120             // JarInputStream "automatically" reads the manifest
 121             if (name.equals("meta-iNf/ManIfEst.Mf"))
 122                 return;
 123             ZipEntry e = jis.getNextEntry();
 124 
 125             byte[] data = (this.data == null) ? new byte[]{} : this.data;
 126             byte[] otherData = new byte[data.length];
 127             jis.read(otherData);
 128             check(Arrays.equals(data, otherData));
 129 
 130             byte[] extra = (this.extra != null && this.extra.length == 0) ?
 131                 null : this.extra;
 132             check(Arrays.equals(extra, e.getExtra()));
 133 
 134             check(name.equals(e.getName()));
 135             check(method == e.getMethod());
 136             check(e.getSize() == -1 || e.getSize() == data.length);
 137             check((method == ZipEntry.DEFLATED) ||
 138                   (e.getCompressedSize() == data.length));
 139 
 140         }
 141     }
 142 
 143     private static int uniquifier = 86;
 144     private static String uniquify(String name) {
 145         return name + (uniquifier++);
 146     }
 147 
 148     private static byte[] toBytes(String s) throws Exception {
 149         return s.getBytes("UTF-8");
 150     }
 151 
 152     private static byte[] toExtra(byte[] bytes) throws Exception {
 153         if (bytes == null) return null;
 154         // Construct a fake extra field with valid header length
 155         byte[] v = new byte[bytes.length + 4];
 156         v[0] = (byte) 0x47;
 157         v[1] = (byte) 0xff;
 158         v[2] = (byte) bytes.length;
 159         v[3] = (byte) (bytes.length << 8);
 160         System.arraycopy(bytes, 0, v, 4, bytes.length);
 161         return v;
 162     }
 163 
 164     private static Random random = new Random();
 165 
 166     private static String makeName(int length) {
 167         StringBuilder sb = new StringBuilder(length);
 168         for (int i = 0; i < length; i++)
 169             sb.append((char)(random.nextInt(10000)+1));
 170         return sb.toString();
 171     }
 172 
 173     public static void main(String[] args) throws Exception {
 174         File zipName = new File("x.zip");
 175         int[]    methods  = {ZipEntry.STORED, ZipEntry.DEFLATED};
 176         String[] names    = {makeName(1), makeName(160), makeName(9000)};
 177         byte[][] datas    = {null, new byte[]{}, new byte[]{'d'}};
 178         byte[][] extras   = {null, new byte[]{}, new byte[]{'e'}};
 179         String[] comments = {null, "", "c"};
 180 
 181         List<Entry> entries = new ArrayList<Entry>();
 182 
 183         // Highly unusual manifest
 184         entries.add(new Entry("meta-iNf/ManIfEst.Mf",
 185                               ZipEntry.STORED,
 186                               toBytes("maNiFest-VeRsIon: 1.0\n"),
 187                               toExtra(toBytes("Can manifests have extra??")),
 188                               "Can manifests have comments??"));
 189 
 190         // The emptiest possible entry
 191         entries.add(new Entry("", ZipEntry.STORED,   null, null, ""));
 192 
 193         for (String name : names)
 194             for (int method : methods)
 195                 for (byte[] data : datas) // datae??
 196                     for (byte[] extra : extras)
 197                         for (String comment : comments)
 198                             entries.add(new Entry(uniquify(name), method, data,
 199                                                   toExtra(extra), comment));
 200 
 201         //----------------------------------------------------------------
 202         // Write zip file using ZipOutputStream
 203         //----------------------------------------------------------------
 204         ZipOutputStream zos = new ZipOutputStream(
 205             new FileOutputStream(zipName));
 206 
 207         for (Entry e : entries)
 208             e.write(zos);
 209 
 210         zos.close();
 211 
 212         //----------------------------------------------------------------
 213         // Verify zip file contents using JarFile class
 214         //----------------------------------------------------------------
 215         JarFile f = new JarFile(zipName);
 216 
 217         check(f.getManifest() != null);
 218 
 219         for (Entry e : entries)
 220             e.verify(f);
 221 
 222         f.close();
 223 
 224         //----------------------------------------------------------------
 225         // Verify zip file contents using JarInputStream class
 226         //----------------------------------------------------------------
 227         JarInputStream jis = new JarInputStream(
 228             new FileInputStream(zipName));
 229 
 230         // JarInputStream "automatically" reads the manifest
 231         check(jis.getManifest() != null);
 232 
 233         for (Entry e : entries)
 234             e.verify(jis);
 235 
 236         jis.close();
 237 
 238 //      String cmd = "unzip -t " + zipName.getPath() + " >/dev/tty";
 239 //      new ProcessBuilder(new String[]{"/bin/sh", "-c", cmd}).start().waitFor();
 240 
 241         zipName.deleteOnExit();
 242 
 243         System.out.printf("passed = %d, failed = %d%n", passed, failed);
 244         if (failed > 0) throw new Exception("Some tests failed");
 245     }
 246 }