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

8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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<>();
  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"); //NOSONAR
  60. } catch (InstantiationException ie) {
  61. throw new RuntimeException(c + " is not a concrete class"); //NOSONAR
  62. } catch (IllegalAccessException ie) {
  63. throw new RuntimeException(c + "\'s no-arg constructor is not public"); //NOSONAR
  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<>();
  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 " + length
  136. + " bytes exceeds remaining data of "
  137. + (data.length - start - WORD) + " bytes.");
  138. case UnparseableExtraField.READ_KEY:
  139. UnparseableExtraFieldData field = new UnparseableExtraFieldData();
  140. if (local) {
  141. field.parseFromLocalFileData(data, start, data.length - start);
  142. } else {
  143. field.parseFromCentralDirectoryData(data, start, data.length - start);
  144. }
  145. v.add(field);
  146. //$FALL-THROUGH$
  147. case UnparseableExtraField.SKIP_KEY:
  148. // since we cannot parse the data we must assume
  149. // the extra field consumes the whole rest of the
  150. // available data
  151. break LOOP;
  152. default:
  153. throw new ZipException("unknown UnparseableExtraField key: "
  154. + onUnparseableData.getKey());
  155. }
  156. }
  157. try {
  158. ZipExtraField ze = createExtraField(headerId);
  159. if (local || !(ze instanceof CentralDirectoryParsingZipExtraField)) {
  160. ze.parseFromLocalFileData(data, start + WORD, length);
  161. } else {
  162. ((CentralDirectoryParsingZipExtraField) ze)
  163. .parseFromCentralDirectoryData(data, start + WORD, length);
  164. }
  165. v.add(ze);
  166. } catch (InstantiationException | IllegalAccessException ie) {
  167. throw new ZipException(ie.getMessage());
  168. }
  169. start += (length + WORD);
  170. }
  171. ZipExtraField[] result = new ZipExtraField[v.size()];
  172. return v.toArray(result);
  173. }
  174. /**
  175. * Merges the local file data fields of the given ZipExtraFields.
  176. * @param data an array of ExtraFiles
  177. * @return an array of bytes
  178. * @since 1.1
  179. */
  180. public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
  181. final boolean lastIsUnparseableHolder = data.length > 0
  182. && data[data.length - 1] instanceof UnparseableExtraFieldData;
  183. int regularExtraFieldCount = lastIsUnparseableHolder ? data.length - 1 : data.length;
  184. int sum = WORD * regularExtraFieldCount;
  185. for (ZipExtraField element : data) {
  186. sum += element.getLocalFileDataLength().getValue();
  187. }
  188. byte[] result = new byte[sum];
  189. int start = 0;
  190. for (int i = 0; i < regularExtraFieldCount; i++) {
  191. System.arraycopy(data[i].getHeaderId().getBytes(),
  192. 0, result, start, 2);
  193. System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
  194. 0, result, start + 2, 2);
  195. byte[] local = data[i].getLocalFileDataData();
  196. System.arraycopy(local, 0, result, start + WORD, local.length);
  197. start += (local.length + WORD);
  198. }
  199. if (lastIsUnparseableHolder) {
  200. byte[] local = data[data.length - 1].getLocalFileDataData();
  201. System.arraycopy(local, 0, result, start, local.length);
  202. }
  203. return result;
  204. }
  205. /**
  206. * Merges the central directory fields of the given ZipExtraFields.
  207. * @param data an array of ExtraFields
  208. * @return an array of bytes
  209. * @since 1.1
  210. */
  211. public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
  212. final boolean lastIsUnparseableHolder = data.length > 0
  213. && data[data.length - 1] instanceof UnparseableExtraFieldData;
  214. int regularExtraFieldCount = lastIsUnparseableHolder ? data.length - 1 : data.length;
  215. int sum = WORD * regularExtraFieldCount;
  216. for (ZipExtraField element : data) {
  217. sum += element.getCentralDirectoryLength().getValue();
  218. }
  219. byte[] result = new byte[sum];
  220. int start = 0;
  221. for (int i = 0; i < regularExtraFieldCount; i++) {
  222. System.arraycopy(data[i].getHeaderId().getBytes(),
  223. 0, result, start, 2);
  224. System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
  225. 0, result, start + 2, 2);
  226. byte[] local = data[i].getCentralDirectoryData();
  227. System.arraycopy(local, 0, result, start + WORD, local.length);
  228. start += (local.length + WORD);
  229. }
  230. if (lastIsUnparseableHolder) {
  231. byte[] local = data[data.length - 1].getCentralDirectoryData();
  232. System.arraycopy(local, 0, result, start, local.length);
  233. }
  234. return result;
  235. }
  236. /**
  237. * "enum" for the possible actions to take if the extra field
  238. * cannot be parsed.
  239. */
  240. public static final class UnparseableExtraField {
  241. /**
  242. * Key for "throw an exception" action.
  243. */
  244. public static final int THROW_KEY = 0;
  245. /**
  246. * Key for "skip" action.
  247. */
  248. public static final int SKIP_KEY = 1;
  249. /**
  250. * Key for "read" action.
  251. */
  252. public static final int READ_KEY = 2;
  253. /**
  254. * Throw an exception if field cannot be parsed.
  255. */
  256. public static final UnparseableExtraField THROW = new UnparseableExtraField(THROW_KEY);
  257. /**
  258. * Skip the extra field entirely and don't make its data
  259. * available - effectively removing the extra field data.
  260. */
  261. public static final UnparseableExtraField SKIP = new UnparseableExtraField(SKIP_KEY);
  262. /**
  263. * Read the extra field data into an instance of {@link
  264. * UnparseableExtraFieldData UnparseableExtraFieldData}.
  265. */
  266. public static final UnparseableExtraField READ = new UnparseableExtraField(READ_KEY);
  267. private final int key;
  268. private UnparseableExtraField(int k) {
  269. key = k;
  270. }
  271. /**
  272. * Key of the action to take.
  273. *
  274. * @return int
  275. */
  276. public int getKey() {
  277. return key;
  278. }
  279. }
  280. }