You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZipUtil.java 9.3 kB

11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.zip;
  19. import java.io.IOException;
  20. import java.util.Calendar;
  21. import java.util.Date;
  22. import java.util.zip.CRC32;
  23. /**
  24. * Utility class for handling DOS and Java time conversions.
  25. * @since Ant 1.8.1
  26. */
  27. public abstract class ZipUtil {
  28. /**
  29. * Smallest date/time ZIP can handle.
  30. */
  31. private static final byte[] DOS_TIME_MIN = ZipLong.getBytes(0x00002100L);
  32. /**
  33. * Convert a Date object to a DOS date/time field.
  34. *
  35. * @param time the <code>Date</code> to convert
  36. * @return the date as a <code>ZipLong</code>
  37. */
  38. public static ZipLong toDosTime(Date time) {
  39. return new ZipLong(toDosTime(time.getTime()));
  40. }
  41. /**
  42. * Convert a Date object to a DOS date/time field.
  43. *
  44. * <p>Stolen from InfoZip's <code>fileio.c</code></p>
  45. *
  46. * @param t number of milliseconds since the epoch
  47. * @return the date as a byte array
  48. */
  49. public static byte[] toDosTime(long t) {
  50. byte[] result = new byte[4];
  51. toDosTime(t, result, 0);
  52. return result;
  53. }
  54. /**
  55. * Convert a Date object to a DOS date/time field.
  56. *
  57. * <p>Stolen from InfoZip's <code>fileio.c</code></p>
  58. *
  59. * @param t number of milliseconds since the epoch
  60. * @param buf the output buffer
  61. * @param offset
  62. * The offset within the output buffer of the first byte to be written.
  63. * must be non-negative and no larger than <tt>buf.length-4</tt>
  64. */
  65. public static void toDosTime(long t, byte[] buf, int offset) {
  66. toDosTime(Calendar.getInstance(), t, buf, offset);
  67. }
  68. static void toDosTime(Calendar c, long t, byte[] buf, int offset) {
  69. c.setTimeInMillis(t);
  70. int year = c.get(Calendar.YEAR);
  71. if (year < 1980) {
  72. System.arraycopy(DOS_TIME_MIN, 0, buf, offset, DOS_TIME_MIN.length); // stop callers from changing the array
  73. return;
  74. }
  75. int month = c.get(Calendar.MONTH) + 1;
  76. long value = ((year - 1980) << 25)
  77. | (month << 21)
  78. | (c.get(Calendar.DAY_OF_MONTH) << 16)
  79. | (c.get(Calendar.HOUR_OF_DAY) << 11)
  80. | (c.get(Calendar.MINUTE) << 5)
  81. | (c.get(Calendar.SECOND) >> 1);
  82. ZipLong.putLong(value, buf, offset);
  83. }
  84. /**
  85. * Assumes a negative integer really is a positive integer that
  86. * has wrapped around and re-creates the original value.
  87. *
  88. * <p>This methods is no longer used as of Apache Ant 1.9.0</p>
  89. *
  90. * @param i the value to treat as unsigned int.
  91. * @return the unsigned int as a long.
  92. */
  93. public static long adjustToLong(int i) {
  94. if (i < 0) {
  95. return 2 * ((long) Integer.MAX_VALUE) + 2 + i;
  96. } else {
  97. return i;
  98. }
  99. }
  100. /**
  101. * Convert a DOS date/time field to a Date object.
  102. *
  103. * @param zipDosTime contains the stored DOS time.
  104. * @return a Date instance corresponding to the given time.
  105. */
  106. public static Date fromDosTime(ZipLong zipDosTime) {
  107. long dosTime = zipDosTime.getValue();
  108. return new Date(dosToJavaTime(dosTime));
  109. }
  110. /**
  111. * Converts DOS time to Java time (number of milliseconds since
  112. * epoch).
  113. *
  114. * @param dosTime long
  115. * @return long
  116. */
  117. public static long dosToJavaTime(long dosTime) {
  118. Calendar cal = Calendar.getInstance();
  119. // CheckStyle:MagicNumberCheck OFF - no point
  120. cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980);
  121. cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1);
  122. cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
  123. cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f);
  124. cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
  125. cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
  126. cal.set(Calendar.MILLISECOND, 0);
  127. // CheckStyle:MagicNumberCheck ON
  128. return cal.getTime().getTime();
  129. }
  130. /**
  131. * If the entry has Unicode*ExtraFields and the CRCs of the
  132. * names/comments match those of the extra fields, transfer the
  133. * known Unicode values from the extra field.
  134. *
  135. * @param ze ZipEntry
  136. * @param originalNameBytes byte[]
  137. * @param commentBytes byte[]
  138. */
  139. static void setNameAndCommentFromExtraFields(ZipEntry ze,
  140. byte[] originalNameBytes,
  141. byte[] commentBytes) {
  142. UnicodePathExtraField name = (UnicodePathExtraField)
  143. ze.getExtraField(UnicodePathExtraField.UPATH_ID);
  144. String originalName = ze.getName();
  145. String newName = getUnicodeStringIfOriginalMatches(name,
  146. originalNameBytes);
  147. if (newName != null && !originalName.equals(newName)) {
  148. ze.setName(newName);
  149. }
  150. if (commentBytes != null && commentBytes.length > 0) {
  151. UnicodeCommentExtraField cmt = (UnicodeCommentExtraField)
  152. ze.getExtraField(UnicodeCommentExtraField.UCOM_ID);
  153. String newComment =
  154. getUnicodeStringIfOriginalMatches(cmt, commentBytes);
  155. if (newComment != null) {
  156. ze.setComment(newComment);
  157. }
  158. }
  159. }
  160. /**
  161. * If the stored CRC matches the one of the given name, return the
  162. * Unicode name of the given field.
  163. *
  164. * <p>If the field is null or the CRCs don't match, return null
  165. * instead.</p>
  166. *
  167. * @param f AbstractUnicodeExtraField
  168. * @param orig byte[]
  169. */
  170. private static
  171. String getUnicodeStringIfOriginalMatches(AbstractUnicodeExtraField f,
  172. byte[] orig) {
  173. if (f != null) {
  174. CRC32 crc32 = new CRC32();
  175. crc32.update(orig);
  176. long origCRC32 = crc32.getValue();
  177. if (origCRC32 == f.getNameCRC32()) {
  178. try {
  179. return ZipEncodingHelper
  180. .UTF8_ZIP_ENCODING.decode(f.getUnicodeName());
  181. } catch (IOException ex) {
  182. // UTF-8 unsupported? should be impossible the
  183. // Unicode*ExtraField must contain some bad bytes
  184. // TODO log this anywhere?
  185. return null;
  186. }
  187. }
  188. }
  189. return null;
  190. }
  191. /**
  192. * Create a copy of the given array - or return null if the
  193. * argument is null.
  194. *
  195. * @param from byte[]
  196. * @return byte[]
  197. */
  198. static byte[] copy(byte[] from) {
  199. if (from != null) {
  200. byte[] to = new byte[from.length];
  201. System.arraycopy(from, 0, to, 0, to.length);
  202. return to;
  203. }
  204. return null;
  205. }
  206. /**
  207. * Whether this library is able to read or write the given entry.
  208. *
  209. * @return boolean
  210. */
  211. static boolean canHandleEntryData(ZipEntry entry) {
  212. return supportsEncryptionOf(entry) && supportsMethodOf(entry);
  213. }
  214. /**
  215. * Whether this library supports the encryption used by the given
  216. * entry.
  217. *
  218. * @return true if the entry isn't encrypted at all
  219. */
  220. private static boolean supportsEncryptionOf(ZipEntry entry) {
  221. return !entry.getGeneralPurposeBit().usesEncryption();
  222. }
  223. /**
  224. * Whether this library supports the compression method used by
  225. * the given entry.
  226. *
  227. * @return true if the compression method is STORED or DEFLATED
  228. */
  229. private static boolean supportsMethodOf(ZipEntry entry) {
  230. return entry.getMethod() == ZipEntry.STORED
  231. || entry.getMethod() == ZipEntry.DEFLATED;
  232. }
  233. /**
  234. * Checks whether the entry requires features not (yet) supported
  235. * by the library and throws an exception if it does.
  236. */
  237. static void checkRequestedFeatures(ZipEntry ze)
  238. throws UnsupportedZipFeatureException {
  239. if (!supportsEncryptionOf(ze)) {
  240. throw
  241. new UnsupportedZipFeatureException(UnsupportedZipFeatureException
  242. .Feature.ENCRYPTION, ze);
  243. }
  244. if (!supportsMethodOf(ze)) {
  245. throw
  246. new UnsupportedZipFeatureException(UnsupportedZipFeatureException
  247. .Feature.METHOD, ze);
  248. }
  249. }
  250. }