1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.compress.archivers.zip;
18
19 import java.io.IOException;
20 import java.math.BigInteger;
21 import java.time.Instant;
22 import java.time.LocalDateTime;
23 import java.time.ZoneId;
24 import java.util.Arrays;
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.zip.CRC32;
28 import java.util.zip.ZipEntry;
29
30
31
32
33
34
35 public abstract class ZipUtil {
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 private static final long DOSTIME_BEFORE_1980 = 1 << 21 | 1 << 16;
78
79
80 private static final long DOSTIME_BEFORE_1980_AS_JAVA_TIME = dosToJavaTime(DOSTIME_BEFORE_1980);
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 private static final long UPPER_DOSTIME_BOUND = 128L * 365 * 24 * 60 * 60 * 1000;
101
102
103
104
105
106
107
108 public static long adjustToLong(final int i) {
109 if (i < 0) {
110 return 2 * (long) Integer.MAX_VALUE + 2 + i;
111 }
112 return i;
113 }
114
115
116
117
118
119
120
121 static long bigToLong(final BigInteger big) {
122 if (big.bitLength() <= 63) {
123 return big.longValue();
124 }
125 throw new NumberFormatException("The BigInteger cannot fit inside a 64 bit java long: [" + big + "]");
126 }
127
128
129
130
131 static boolean canHandleEntryData(final ZipArchiveEntry entry) {
132 return supportsEncryptionOf(entry) && supportsMethodOf(entry);
133 }
134
135
136
137
138 static void checkRequestedFeatures(final ZipArchiveEntry ze) throws UnsupportedZipFeatureException {
139 if (!supportsEncryptionOf(ze)) {
140 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.ENCRYPTION, ze);
141 }
142 if (!supportsMethodOf(ze)) {
143 final ZipMethod m = ZipMethod.getMethodByCode(ze.getMethod());
144 if (m == null) {
145 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.METHOD, ze);
146 }
147 throw new UnsupportedZipFeatureException(m, ze);
148 }
149 }
150
151
152
153
154 static byte[] copy(final byte[] from) {
155 if (from != null) {
156 return Arrays.copyOf(from, from.length);
157 }
158 return null;
159 }
160
161 static void copy(final byte[] from, final byte[] to, final int offset) {
162 if (from != null) {
163 System.arraycopy(from, 0, to, offset, from.length);
164 }
165 }
166
167 private static Date dosToJavaDate(final long dosTime) {
168 final Calendar cal = Calendar.getInstance();
169
170 cal.set(Calendar.YEAR, (int) (dosTime >> 25 & 0x7f) + 1980);
171 cal.set(Calendar.MONTH, (int) (dosTime >> 21 & 0x0f) - 1);
172 cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
173 cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f);
174 cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
175 cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
176 cal.set(Calendar.MILLISECOND, 0);
177
178 return cal.getTime();
179 }
180
181
182
183
184
185
186
187 public static long dosToJavaTime(final long dosTime) {
188 return dosToJavaDate(dosTime).getTime();
189 }
190
191
192
193
194
195
196
197 public static Date fromDosTime(final ZipLong zipDosTime) {
198 final long dosTime = zipDosTime.getValue();
199 return dosToJavaDate(dosTime);
200 }
201
202
203
204
205
206
207
208
209 private static String getUnicodeStringIfOriginalMatches(final AbstractUnicodeExtraField f, final byte[] orig) {
210 if (f != null) {
211 final CRC32 crc32 = new CRC32();
212 crc32.update(orig);
213 final long origCRC32 = crc32.getValue();
214
215 if (origCRC32 == f.getNameCRC32()) {
216 try {
217 return ZipEncodingHelper.ZIP_ENCODING_UTF_8.decode(f.getUnicodeName());
218 } catch (final IOException ex) {
219
220
221 }
222 }
223 }
224
225 return null;
226 }
227
228
229
230
231
232
233
234
235 public static boolean isDosTime(final long time) {
236 return time <= UPPER_DOSTIME_BOUND &&
237 (time == DOSTIME_BEFORE_1980_AS_JAVA_TIME || javaToDosTime(time) != DOSTIME_BEFORE_1980);
238 }
239
240 private static LocalDateTime javaEpochToLocalDateTime(final long time) {
241 final Instant instant = Instant.ofEpochMilli(time);
242 return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
243 }
244
245
246 private static long javaToDosTime(final long t) {
247 final LocalDateTime ldt = javaEpochToLocalDateTime(t);
248 if (ldt.getYear() < 1980) {
249 return DOSTIME_BEFORE_1980;
250 }
251 return (ldt.getYear() - 1980 << 25 | ldt.getMonthValue() << 21 | ldt.getDayOfMonth() << 16 | ldt.getHour() << 11 | ldt.getMinute() << 5
252 | ldt.getSecond() >> 1) & 0xffffffffL;
253 }
254
255
256
257
258
259
260
261
262
263
264 static BigInteger longToBig(long l) {
265 if (l < Integer.MIN_VALUE) {
266 throw new IllegalArgumentException("Negative longs < -2^31 not permitted: [" + l + "]");
267 }
268 if (l < 0 && l >= Integer.MIN_VALUE) {
269
270
271 l = ZipUtil.adjustToLong((int) l);
272 }
273 return BigInteger.valueOf(l);
274 }
275
276
277
278
279
280
281
282
283
284 public static byte[] reverse(final byte[] array) {
285 final int z = array.length - 1;
286 for (int i = 0; i < array.length / 2; i++) {
287 final byte x = array[i];
288 array[i] = array[z - i];
289 array[z - i] = x;
290 }
291 return array;
292 }
293
294
295
296
297
298 static void setNameAndCommentFromExtraFields(final ZipArchiveEntry ze, final byte[] originalNameBytes, final byte[] commentBytes) {
299 final ZipExtraField nameCandidate = ze.getExtraField(UnicodePathExtraField.UPATH_ID);
300 final UnicodePathExtraField name = nameCandidate instanceof UnicodePathExtraField ? (UnicodePathExtraField) nameCandidate : null;
301 final String newName = getUnicodeStringIfOriginalMatches(name, originalNameBytes);
302 if (newName != null) {
303 ze.setName(newName);
304 ze.setNameSource(ZipArchiveEntry.NameSource.UNICODE_EXTRA_FIELD);
305 }
306
307 if (commentBytes != null && commentBytes.length > 0) {
308 final ZipExtraField cmtCandidate = ze.getExtraField(UnicodeCommentExtraField.UCOM_ID);
309 final UnicodeCommentExtraField cmt = cmtCandidate instanceof UnicodeCommentExtraField ? (UnicodeCommentExtraField) cmtCandidate : null;
310 final String newComment = getUnicodeStringIfOriginalMatches(cmt, commentBytes);
311 if (newComment != null) {
312 ze.setComment(newComment);
313 ze.setCommentSource(ZipArchiveEntry.CommentSource.UNICODE_EXTRA_FIELD);
314 }
315 }
316 }
317
318
319
320
321
322
323
324
325 public static int signedByteToUnsignedInt(final byte b) {
326 if (b >= 0) {
327 return b;
328 }
329 return 256 + b;
330 }
331
332
333
334
335
336
337 private static boolean supportsEncryptionOf(final ZipArchiveEntry entry) {
338 return !entry.getGeneralPurposeBit().usesEncryption();
339 }
340
341
342
343
344
345
346 private static boolean supportsMethodOf(final ZipArchiveEntry entry) {
347 return entry.getMethod() == ZipEntry.STORED || entry.getMethod() == ZipMethod.UNSHRINKING.getCode()
348 || entry.getMethod() == ZipMethod.IMPLODING.getCode() || entry.getMethod() == ZipEntry.DEFLATED
349 || entry.getMethod() == ZipMethod.ENHANCED_DEFLATED.getCode() || entry.getMethod() == ZipMethod.BZIP2.getCode();
350 }
351
352
353
354
355
356
357
358 public static ZipLong toDosTime(final Date time) {
359 return new ZipLong(toDosTime(time.getTime()));
360 }
361
362
363
364
365
366
367
368
369
370
371
372 public static byte[] toDosTime(final long t) {
373 final byte[] result = new byte[4];
374 toDosTime(t, result, 0);
375 return result;
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389 public static void toDosTime(final long t, final byte[] buf, final int offset) {
390 ZipLong.putLong(javaToDosTime(t), buf, offset);
391 }
392
393
394
395
396
397
398
399
400
401 public static byte unsignedIntToSignedByte(final int i) {
402 if (i > 255 || i < 0) {
403 throw new IllegalArgumentException("Can only convert non-negative integers between [0,255] to byte: [" + i + "]");
404 }
405 if (i < 128) {
406 return (byte) i;
407 }
408 return (byte) (i - 256);
409 }
410 }