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.

ExtraFieldUtils.java 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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.util.ArrayList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.concurrent.ConcurrentHashMap;
  23. import java.util.zip.ZipException;
  24. /**
  25. * ZipExtraField related methods
  26. *
  27. */
  28. // CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
  29. public class ExtraFieldUtils {
  30. private static final int WORD = 4;
  31. /**
  32. * Static registry of known extra fields.
  33. *
  34. * @since 1.1
  35. */
  36. private static final Map<ZipShort, Class<?>> implementations;
  37. static {
  38. implementations = new ConcurrentHashMap<ZipShort, Class<?>>();
  39. register(AsiExtraField.class);
  40. register(JarMarker.class);
  41. register(UnicodePathExtraField.class);
  42. register(UnicodeCommentExtraField.class);
  43. register(Zip64ExtendedInformationExtraField.class);
  44. }
  45. /**
  46. * Register a ZipExtraField implementation.
  47. *
  48. * <p>The given class must have a no-arg constructor and implement
  49. * the {@link ZipExtraField ZipExtraField interface}.</p>
  50. * @param c the class to register
  51. *
  52. * @since 1.1
  53. */
  54. public static void register(Class<?> c) {
  55. try {
  56. ZipExtraField ze = (ZipExtraField) c.newInstance();
  57. implementations.put(ze.getHeaderId(), c);
  58. } catch (ClassCastException cc) {
  59. throw new RuntimeException(c + " doesn\'t implement ZipExtraField");
  60. } catch (InstantiationException ie) {
  61. throw new RuntimeException(c + " is not a concrete class");
  62. } catch (IllegalAccessException ie) {
  63. throw new RuntimeException(c + "\'s no-arg constructor is not public");
  64. }
  65. }
  66. /**
  67. * Create an instance of the appropriate ExtraField, falls back to
  68. * {@link UnrecognizedExtraField UnrecognizedExtraField}.
  69. * @param headerId the header identifier
  70. * @return an instance of the appropriate ExtraField
  71. * @exception InstantiationException if unable to instantiate the class
  72. * @exception IllegalAccessException if not allowed to instantiate the class
  73. * @since 1.1
  74. */
  75. public static ZipExtraField createExtraField(ZipShort headerId)
  76. throws InstantiationException, IllegalAccessException {
  77. Class<?> c = implementations.get(headerId);
  78. if (c != null) {
  79. return (ZipExtraField) c.newInstance();
  80. }
  81. UnrecognizedExtraField u = new UnrecognizedExtraField();
  82. u.setHeaderId(headerId);
  83. return u;
  84. }
  85. /**
  86. * Split the array into ExtraFields and populate them with the
  87. * given data as local file data, throwing an exception if the
  88. * data cannot be parsed.
  89. * @param data an array of bytes as it appears in local file data
  90. * @return an array of ExtraFields
  91. * @throws ZipException on error
  92. */
  93. public static ZipExtraField[] parse(byte[] data) throws ZipException {
  94. return parse(data, true, UnparseableExtraField.THROW);
  95. }
  96. /**
  97. * Split the array into ExtraFields and populate them with the
  98. * given data, throwing an exception if the data cannot be parsed.
  99. * @param data an array of bytes
  100. * @param local whether data originates from the local file data
  101. * or the central directory
  102. * @return an array of ExtraFields
  103. * @since 1.1
  104. * @throws ZipException on error
  105. */
  106. public static ZipExtraField[] parse(byte[] data, boolean local)
  107. throws ZipException {
  108. return parse(data, local, UnparseableExtraField.THROW);
  109. }
  110. /**
  111. * Split the array into ExtraFields and populate them with the
  112. * given data.
  113. * @param data an array of bytes
  114. * @param local whether data originates from the local file data
  115. * or the central directory
  116. * @param onUnparseableData what to do if the extra field data
  117. * cannot be parsed.
  118. * @return an array of ExtraFields
  119. * @throws ZipException on error
  120. * @since Ant 1.8.1
  121. */
  122. public static ZipExtraField[] parse(byte[] data, boolean local,
  123. UnparseableExtraField onUnparseableData)
  124. throws ZipException {
  125. List<ZipExtraField> v = new ArrayList<ZipExtraField>();
  126. int start = 0;
  127. LOOP:
  128. while (start <= data.length - WORD) {
  129. ZipShort headerId = new ZipShort(data, start);
  130. int length = (new ZipShort(data, start + 2)).getValue();
  131. if (start + WORD + length > data.length) {
  132. switch(onUnparseableData.getKey()) {
  133. case UnparseableExtraField.THROW_KEY:
  134. throw new ZipException("bad extra field starting at "
  135. + start + ". Block length of "
  136. + length + " bytes exceeds remaining"
  137. + " data of "
  138. + (data.length - start - WORD)
  139. + " bytes.");
  140. case UnparseableExtraField.READ_KEY:
  141. UnparseableExtraFieldData field =
  142. new UnparseableExtraFieldData();
  143. if (local) {
  144. field.parseFromLocalFileData(data, start,
  145. data.length - start);
  146. } else {
  147. field.parseFromCentralDirectoryData(data, start,
  148. data.length - start);
  149. }
  150. v.add(field);
  151. //$FALL-THROUGH$
  152. case UnparseableExtraField.SKIP_KEY:
  153. // since we cannot parse the data we must assume
  154. // the extra field consumes the whole rest of the
  155. // available data
  156. break LOOP;
  157. default:
  158. throw new ZipException("unknown UnparseableExtraField key: "
  159. + onUnparseableData.getKey());
  160. }
  161. }
  162. try {
  163. ZipExtraField ze = createExtraField(headerId);
  164. if (local
  165. || !(ze instanceof CentralDirectoryParsingZipExtraField)) {
  166. ze.parseFromLocalFileData(data, start + WORD, length);
  167. } else {
  168. ((CentralDirectoryParsingZipExtraField) ze)
  169. .parseFromCentralDirectoryData(data, start + WORD,
  170. length);
  171. }
  172. v.add(ze);
  173. } catch (InstantiationException ie) {
  174. throw new ZipException(ie.getMessage());
  175. } catch (IllegalAccessException iae) {
  176. throw new ZipException(iae.getMessage());
  177. }
  178. start += (length + WORD);
  179. }
  180. ZipExtraField[] result = new ZipExtraField[v.size()];
  181. return v.toArray(result);
  182. }
  183. /**
  184. * Merges the local file data fields of the given ZipExtraFields.
  185. * @param data an array of ExtraFiles
  186. * @return an array of bytes
  187. * @since 1.1
  188. */
  189. public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
  190. final boolean lastIsUnparseableHolder = data.length > 0
  191. && data[data.length - 1] instanceof UnparseableExtraFieldData;
  192. int regularExtraFieldCount =
  193. lastIsUnparseableHolder ? data.length - 1 : data.length;
  194. int sum = WORD * regularExtraFieldCount;
  195. for (ZipExtraField element : data) {
  196. sum += element.getLocalFileDataLength().getValue();
  197. }
  198. byte[] result = new byte[sum];
  199. int start = 0;
  200. for (int i = 0; i < regularExtraFieldCount; i++) {
  201. System.arraycopy(data[i].getHeaderId().getBytes(),
  202. 0, result, start, 2);
  203. System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
  204. 0, result, start + 2, 2);
  205. byte[] local = data[i].getLocalFileDataData();
  206. System.arraycopy(local, 0, result, start + WORD, local.length);
  207. start += (local.length + WORD);
  208. }
  209. if (lastIsUnparseableHolder) {
  210. byte[] local = data[data.length - 1].getLocalFileDataData();
  211. System.arraycopy(local, 0, result, start, local.length);
  212. }
  213. return result;
  214. }
  215. /**
  216. * Merges the central directory fields of the given ZipExtraFields.
  217. * @param data an array of ExtraFields
  218. * @return an array of bytes
  219. * @since 1.1
  220. */
  221. public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
  222. final boolean lastIsUnparseableHolder = data.length > 0
  223. && data[data.length - 1] instanceof UnparseableExtraFieldData;
  224. int regularExtraFieldCount =
  225. lastIsUnparseableHolder ? data.length - 1 : data.length;
  226. int sum = WORD * regularExtraFieldCount;
  227. for (ZipExtraField element : data) {
  228. sum += element.getCentralDirectoryLength().getValue();
  229. }
  230. byte[] result = new byte[sum];
  231. int start = 0;
  232. for (int i = 0; i < regularExtraFieldCount; i++) {
  233. System.arraycopy(data[i].getHeaderId().getBytes(),
  234. 0, result, start, 2);
  235. System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
  236. 0, result, start + 2, 2);
  237. byte[] local = data[i].getCentralDirectoryData();
  238. System.arraycopy(local, 0, result, start + WORD, local.length);
  239. start += (local.length + WORD);
  240. }
  241. if (lastIsUnparseableHolder) {
  242. byte[] local = data[data.length - 1].getCentralDirectoryData();
  243. System.arraycopy(local, 0, result, start, local.length);
  244. }
  245. return result;
  246. }
  247. /**
  248. * "enum" for the possible actions to take if the extra field
  249. * cannot be parsed.
  250. */
  251. public static final class UnparseableExtraField {
  252. /**
  253. * Key for "throw an exception" action.
  254. */
  255. public static final int THROW_KEY = 0;
  256. /**
  257. * Key for "skip" action.
  258. */
  259. public static final int SKIP_KEY = 1;
  260. /**
  261. * Key for "read" action.
  262. */
  263. public static final int READ_KEY = 2;
  264. /**
  265. * Throw an exception if field cannot be parsed.
  266. */
  267. public static final UnparseableExtraField THROW
  268. = new UnparseableExtraField(THROW_KEY);
  269. /**
  270. * Skip the extra field entirely and don't make its data
  271. * available - effectively removing the extra field data.
  272. */
  273. public static final UnparseableExtraField SKIP
  274. = new UnparseableExtraField(SKIP_KEY);
  275. /**
  276. * Read the extra field data into an instance of {@link
  277. * UnparseableExtraFieldData UnparseableExtraFieldData}.
  278. */
  279. public static final UnparseableExtraField READ
  280. = new UnparseableExtraField(READ_KEY);
  281. private final int key;
  282. private UnparseableExtraField(int k) {
  283. key = k;
  284. }
  285. /**
  286. * Key of the action to take.
  287. */
  288. public int getKey() { return key; }
  289. }
  290. }