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.File;
20 import java.io.IOException;
21 import java.nio.file.Files;
22 import java.nio.file.LinkOption;
23 import java.nio.file.Path;
24 import java.nio.file.attribute.BasicFileAttributes;
25 import java.nio.file.attribute.FileTime;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Date;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.NoSuchElementException;
32 import java.util.Objects;
33 import java.util.function.Function;
34 import java.util.zip.ZipEntry;
35 import java.util.zip.ZipException;
36
37 import org.apache.commons.compress.archivers.ArchiveEntry;
38 import org.apache.commons.compress.archivers.EntryStreamOffsets;
39 import org.apache.commons.compress.utils.ByteUtils;
40 import org.apache.commons.io.file.attribute.FileTimes;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class ZipArchiveEntry extends ZipEntry implements ArchiveEntry, EntryStreamOffsets {
62
63
64
65
66
67
68 public enum CommentSource {
69
70
71
72
73 COMMENT,
74
75
76
77 UNICODE_EXTRA_FIELD
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public enum ExtraFieldParsingMode implements ExtraFieldParsingBehavior {
95
96
97
98
99
100
101
102
103
104
105
106
107 BEST_EFFORT(ExtraFieldUtils.UnparseableExtraField.READ) {
108 @Override
109 public ZipExtraField fill(final ZipExtraField field, final byte[] data, final int off, final int len, final boolean local) {
110 return fillAndMakeUnrecognizedOnError(field, data, off, len, local);
111 }
112 },
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 STRICT_FOR_KNOW_EXTRA_FIELDS(ExtraFieldUtils.UnparseableExtraField.READ),
129
130
131
132
133
134
135
136
137 ONLY_PARSEABLE_LENIENT(ExtraFieldUtils.UnparseableExtraField.SKIP) {
138 @Override
139 public ZipExtraField fill(final ZipExtraField field, final byte[] data, final int off, final int len, final boolean local) {
140 return fillAndMakeUnrecognizedOnError(field, data, off, len, local);
141 }
142 },
143
144
145
146
147
148
149
150
151
152
153
154 ONLY_PARSEABLE_STRICT(ExtraFieldUtils.UnparseableExtraField.SKIP),
155
156
157
158 DRACONIC(ExtraFieldUtils.UnparseableExtraField.THROW);
159
160 private static ZipExtraField fillAndMakeUnrecognizedOnError(final ZipExtraField field, final byte[] data, final int off, final int len,
161 final boolean local) {
162 try {
163 return ExtraFieldUtils.fillExtraField(field, data, off, len, local);
164 } catch (final ZipException ex) {
165 final UnrecognizedExtraField u = new UnrecognizedExtraField();
166 u.setHeaderId(field.getHeaderId());
167 if (local) {
168 u.setLocalFileDataData(Arrays.copyOfRange(data, off, off + len));
169 } else {
170 u.setCentralDirectoryData(Arrays.copyOfRange(data, off, off + len));
171 }
172 return u;
173 }
174 }
175
176 private final ExtraFieldUtils.UnparseableExtraField onUnparseableData;
177
178 ExtraFieldParsingMode(final ExtraFieldUtils.UnparseableExtraField onUnparseableData) {
179 this.onUnparseableData = onUnparseableData;
180 }
181
182 @Override
183 public ZipExtraField createExtraField(final ZipShort headerId) {
184 return ExtraFieldUtils.createExtraField(headerId);
185 }
186
187 @Override
188 public ZipExtraField fill(final ZipExtraField field, final byte[] data, final int off, final int len, final boolean local) throws ZipException {
189 return ExtraFieldUtils.fillExtraField(field, data, off, len, local);
190 }
191
192 @Override
193 public ZipExtraField onUnparseableExtraField(final byte[] data, final int off, final int len, final boolean local, final int claimedLength)
194 throws ZipException {
195 return onUnparseableData.onUnparseableExtraField(data, off, len, local, claimedLength);
196 }
197 }
198
199
200
201
202
203
204 public enum NameSource {
205
206
207
208
209 NAME,
210
211
212
213 NAME_WITH_EFS_FLAG,
214
215
216
217 UNICODE_EXTRA_FIELD
218 }
219
220 private static final String ZIP_DIR_SEP = "/";
221
222 static final ZipArchiveEntry[] EMPTY_ARRAY = {};
223 static LinkedList<ZipArchiveEntry> EMPTY_LINKED_LIST = new LinkedList<>();
224
225 public static final int PLATFORM_UNIX = 3;
226 public static final int PLATFORM_FAT = 0;
227 public static final int CRC_UNKNOWN = -1;
228
229 private static final int SHORT_MASK = 0xFFFF;
230
231 private static final int SHORT_SHIFT = 16;
232
233 private static boolean canConvertToInfoZipExtendedTimestamp(final FileTime lastModifiedTime, final FileTime lastAccessTime, final FileTime creationTime) {
234 return FileTimes.isUnixTime(lastModifiedTime) && FileTimes.isUnixTime(lastAccessTime) && FileTimes.isUnixTime(creationTime);
235 }
236
237 private static boolean isDirectoryEntryName(final String entryName) {
238 return entryName.endsWith(ZIP_DIR_SEP);
239 }
240
241 private static String toDirectoryEntryName(final String entryName) {
242 return isDirectoryEntryName(entryName) ? entryName : entryName + ZIP_DIR_SEP;
243 }
244
245 private static String toEntryName(final File inputFile, final String entryName) {
246 return inputFile.isDirectory() ? toDirectoryEntryName(entryName) : entryName;
247 }
248
249 private static String toEntryName(final Path inputPath, final String entryName, final LinkOption... options) {
250 return Files.isDirectory(inputPath, options) ? toDirectoryEntryName(entryName) : entryName;
251 }
252
253
254
255
256
257
258
259
260
261 private int method = ZipMethod.UNKNOWN_CODE;
262
263
264
265
266
267
268
269
270 private long size = SIZE_UNKNOWN;
271 private int internalAttributes;
272 private int versionRequired;
273 private int versionMadeBy;
274 private int platform = PLATFORM_FAT;
275 private int rawFlag;
276 private long externalAttributes;
277 private int alignment;
278 private ZipExtraField[] extraFields;
279 private UnparseableExtraFieldData unparseableExtra;
280 private String name;
281 private byte[] rawName;
282 private GeneralPurposeBit generalPurposeBit = new GeneralPurposeBit();
283 private long localHeaderOffset = OFFSET_UNKNOWN;
284 private long dataOffset = OFFSET_UNKNOWN;
285 private boolean isStreamContiguous;
286
287 private NameSource nameSource = NameSource.NAME;
288
289 private final Function<ZipShort, ZipExtraField> extraFieldFactory;
290
291 private CommentSource commentSource = CommentSource.COMMENT;
292
293 private long diskNumberStart;
294
295 private boolean lastModifiedDateSet;
296
297 private long time = -1;
298
299
300
301 protected ZipArchiveEntry() {
302 this("");
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316 public ZipArchiveEntry(final File inputFile, final String entryName) {
317 this(null, inputFile, entryName);
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332 private ZipArchiveEntry(final Function<ZipShort, ZipExtraField> extraFieldFactory, final File inputFile, final String entryName) {
333 this(extraFieldFactory, toEntryName(inputFile, entryName));
334 try {
335 setAttributes(inputFile.toPath());
336 } catch (final IOException e) {
337 if (inputFile.isFile()) {
338 setSize(inputFile.length());
339 }
340 setTime(inputFile.lastModified());
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358 private ZipArchiveEntry(final Function<ZipShort, ZipExtraField> extraFieldFactory, final Path inputPath, final String entryName,
359 final LinkOption... options) throws IOException {
360 this(extraFieldFactory, toEntryName(inputPath, entryName, options));
361 setAttributes(inputPath, options);
362 }
363
364
365
366
367
368
369
370
371
372
373
374 private ZipArchiveEntry(final Function<ZipShort, ZipExtraField> extraFieldFactory, final String name) {
375 super(name);
376 this.extraFieldFactory = extraFieldFactory;
377 setName(name);
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391 private ZipArchiveEntry(final Function<ZipShort, ZipExtraField> extraFieldFactory, final ZipEntry entry) throws ZipException {
392 super(entry);
393 this.extraFieldFactory = extraFieldFactory;
394 setName(entry.getName());
395 final byte[] extra = entry.getExtra();
396 if (extra != null) {
397 setExtraFields(parseExtraFields(extra, true, ExtraFieldParsingMode.BEST_EFFORT));
398 } else {
399
400 setExtra();
401 }
402 setMethod(entry.getMethod());
403 this.size = entry.getSize();
404 }
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public ZipArchiveEntry(final Path inputPath, final String entryName, final LinkOption... options) throws IOException {
421 this(null, inputPath, entryName, options);
422 }
423
424
425
426
427
428
429
430
431
432
433
434 public ZipArchiveEntry(final String name) {
435 this((Function<ZipShort, ZipExtraField>) null, name);
436 }
437
438
439
440
441
442
443
444
445
446
447
448 public ZipArchiveEntry(final ZipArchiveEntry entry) throws ZipException {
449 this((ZipEntry) entry);
450 setInternalAttributes(entry.getInternalAttributes());
451 setExternalAttributes(entry.getExternalAttributes());
452 setExtraFields(entry.getAllExtraFieldsNoCopy());
453 setPlatform(entry.getPlatform());
454 final GeneralPurposeBit other = entry.getGeneralPurposeBit();
455 setGeneralPurposeBit(other == null ? null : (GeneralPurposeBit) other.clone());
456 }
457
458
459
460
461
462
463
464
465
466
467
468 public ZipArchiveEntry(final ZipEntry entry) throws ZipException {
469 this(null, entry);
470 }
471
472
473
474
475
476
477
478
479
480
481 public void addAsFirstExtraField(final ZipExtraField ze) {
482 if (ze instanceof UnparseableExtraFieldData) {
483 unparseableExtra = (UnparseableExtraFieldData) ze;
484 } else {
485 if (getExtraField(ze.getHeaderId()) != null) {
486 internalRemoveExtraField(ze.getHeaderId());
487 }
488 final ZipExtraField[] copy = extraFields;
489 final int newLen = extraFields != null ? extraFields.length + 1 : 1;
490 extraFields = new ZipExtraField[newLen];
491 extraFields[0] = ze;
492 if (copy != null) {
493 System.arraycopy(copy, 0, extraFields, 1, extraFields.length - 1);
494 }
495 }
496 setExtra();
497 }
498
499
500
501
502
503
504
505
506
507
508 public void addExtraField(final ZipExtraField ze) {
509 internalAddExtraField(ze);
510 setExtra();
511 }
512
513 private void addInfoZipExtendedTimestamp(final FileTime lastModifiedTime, final FileTime lastAccessTime, final FileTime creationTime) {
514 final X5455_ExtendedTimestamp infoZipTimestamp = new X5455_ExtendedTimestamp();
515 if (lastModifiedTime != null) {
516 infoZipTimestamp.setModifyFileTime(lastModifiedTime);
517 }
518 if (lastAccessTime != null) {
519 infoZipTimestamp.setAccessFileTime(lastAccessTime);
520 }
521 if (creationTime != null) {
522 infoZipTimestamp.setCreateFileTime(creationTime);
523 }
524 internalAddExtraField(infoZipTimestamp);
525 }
526
527 private void addNTFSTimestamp(final FileTime lastModifiedTime, final FileTime lastAccessTime, final FileTime creationTime) {
528 final X000A_NTFS ntfsTimestamp = new X000A_NTFS();
529 if (lastModifiedTime != null) {
530 ntfsTimestamp.setModifyFileTime(lastModifiedTime);
531 }
532 if (lastAccessTime != null) {
533 ntfsTimestamp.setAccessFileTime(lastAccessTime);
534 }
535 if (creationTime != null) {
536 ntfsTimestamp.setCreateFileTime(creationTime);
537 }
538 internalAddExtraField(ntfsTimestamp);
539 }
540
541
542
543
544
545
546 @Override
547 public Object clone() {
548 final ZipArchiveEntry e = (ZipArchiveEntry) super.clone();
549
550 e.setInternalAttributes(getInternalAttributes());
551 e.setExternalAttributes(getExternalAttributes());
552 e.setExtraFields(getAllExtraFieldsNoCopy());
553 return e;
554 }
555
556 private ZipExtraField[] copyOf(final ZipExtraField[] src, final int length) {
557 return Arrays.copyOf(src, length);
558 }
559
560 @Override
561 public boolean equals(final Object obj) {
562 if (this == obj) {
563 return true;
564 }
565 if (obj == null || getClass() != obj.getClass()) {
566 return false;
567 }
568 final ZipArchiveEntry other = (ZipArchiveEntry) obj;
569 final String myName = getName();
570 final String otherName = other.getName();
571 if (!Objects.equals(myName, otherName)) {
572 return false;
573 }
574 String myComment = getComment();
575 String otherComment = other.getComment();
576 if (myComment == null) {
577 myComment = "";
578 }
579 if (otherComment == null) {
580 otherComment = "";
581 }
582 return Objects.equals(getLastModifiedTime(), other.getLastModifiedTime()) && Objects.equals(getLastAccessTime(), other.getLastAccessTime())
583 && Objects.equals(getCreationTime(), other.getCreationTime()) && myComment.equals(otherComment)
584 && getInternalAttributes() == other.getInternalAttributes() && getPlatform() == other.getPlatform()
585 && getExternalAttributes() == other.getExternalAttributes() && getMethod() == other.getMethod() && getSize() == other.getSize()
586 && getCrc() == other.getCrc() && getCompressedSize() == other.getCompressedSize()
587 && Arrays.equals(getCentralDirectoryExtra(), other.getCentralDirectoryExtra())
588 && Arrays.equals(getLocalFileDataExtra(), other.getLocalFileDataExtra()) && localHeaderOffset == other.localHeaderOffset
589 && dataOffset == other.dataOffset && generalPurposeBit.equals(other.generalPurposeBit);
590 }
591
592 private ZipExtraField findMatching(final ZipShort headerId, final List<ZipExtraField> fs) {
593 return fs.stream().filter(f -> headerId.equals(f.getHeaderId())).findFirst().orElse(null);
594 }
595
596 private ZipExtraField findUnparseable(final List<ZipExtraField> fs) {
597 return fs.stream().filter(UnparseableExtraFieldData.class::isInstance).findFirst().orElse(null);
598 }
599
600
601
602
603
604
605
606 protected int getAlignment() {
607 return this.alignment;
608 }
609
610 private ZipExtraField[] getAllExtraFields() {
611 final ZipExtraField[] allExtraFieldsNoCopy = getAllExtraFieldsNoCopy();
612 return allExtraFieldsNoCopy == extraFields ? copyOf(allExtraFieldsNoCopy, allExtraFieldsNoCopy.length) : allExtraFieldsNoCopy;
613 }
614
615
616
617
618
619
620 private ZipExtraField[] getAllExtraFieldsNoCopy() {
621 if (extraFields == null) {
622 return getUnparseableOnly();
623 }
624 return unparseableExtra != null ? getMergedFields() : extraFields;
625 }
626
627
628
629
630
631
632 public byte[] getCentralDirectoryExtra() {
633 return ExtraFieldUtils.mergeCentralDirectoryData(getAllExtraFieldsNoCopy());
634 }
635
636
637
638
639
640
641
642 public CommentSource getCommentSource() {
643 return commentSource;
644 }
645
646 @Override
647 public long getDataOffset() {
648 return dataOffset;
649 }
650
651
652
653
654
655
656
657 public long getDiskNumberStart() {
658 return diskNumberStart;
659 }
660
661
662
663
664
665
666
667
668
669
670 public long getExternalAttributes() {
671 return externalAttributes;
672 }
673
674
675
676
677
678
679
680 public ZipExtraField getExtraField(final ZipShort type) {
681 if (extraFields != null) {
682 for (final ZipExtraField extraField : extraFields) {
683 if (type.equals(extraField.getHeaderId())) {
684 return extraField;
685 }
686 }
687 }
688 return null;
689 }
690
691
692
693
694
695
696
697
698
699
700
701 public ZipExtraField[] getExtraFields() {
702 return getParseableExtraFields();
703 }
704
705
706
707
708
709
710
711
712
713 public ZipExtraField[] getExtraFields(final boolean includeUnparseable) {
714 return includeUnparseable ? getAllExtraFields() : getParseableExtraFields();
715 }
716
717
718
719
720
721
722
723
724
725
726 public ZipExtraField[] getExtraFields(final ExtraFieldParsingBehavior parsingBehavior) throws ZipException {
727 if (parsingBehavior == ExtraFieldParsingMode.BEST_EFFORT) {
728 return getExtraFields(true);
729 }
730 if (parsingBehavior == ExtraFieldParsingMode.ONLY_PARSEABLE_LENIENT) {
731 return getExtraFields(false);
732 }
733 final byte[] local = getExtra();
734 final List<ZipExtraField> localFields = new ArrayList<>(Arrays.asList(parseExtraFields(local, true, parsingBehavior)));
735 final byte[] central = getCentralDirectoryExtra();
736 final List<ZipExtraField> centralFields = new ArrayList<>(Arrays.asList(parseExtraFields(central, false, parsingBehavior)));
737 final List<ZipExtraField> merged = new ArrayList<>();
738 for (final ZipExtraField l : localFields) {
739 ZipExtraField c;
740 if (l instanceof UnparseableExtraFieldData) {
741 c = findUnparseable(centralFields);
742 } else {
743 c = findMatching(l.getHeaderId(), centralFields);
744 }
745 if (c != null) {
746 final byte[] cd = c.getCentralDirectoryData();
747 if (cd != null && cd.length > 0) {
748 l.parseFromCentralDirectoryData(cd, 0, cd.length);
749 }
750 centralFields.remove(c);
751 }
752 merged.add(l);
753 }
754 merged.addAll(centralFields);
755 return merged.toArray(ExtraFieldUtils.EMPTY_ZIP_EXTRA_FIELD_ARRAY);
756 }
757
758
759
760
761
762
763
764 public GeneralPurposeBit getGeneralPurposeBit() {
765 return generalPurposeBit;
766 }
767
768
769
770
771
772
773
774
775
776
777 public int getInternalAttributes() {
778 return internalAttributes;
779 }
780
781
782
783
784
785
786
787
788
789 @Override
790 public Date getLastModifiedDate() {
791 return new Date(getTime());
792 }
793
794
795
796
797
798
799 public byte[] getLocalFileDataExtra() {
800 final byte[] extra = getExtra();
801 return extra != null ? extra : ByteUtils.EMPTY_BYTE_ARRAY;
802 }
803
804
805
806
807
808
809
810 public long getLocalHeaderOffset() {
811 return this.localHeaderOffset;
812 }
813
814 private ZipExtraField[] getMergedFields() {
815 final ZipExtraField[] zipExtraFields = copyOf(extraFields, extraFields.length + 1);
816 zipExtraFields[extraFields.length] = unparseableExtra;
817 return zipExtraFields;
818 }
819
820
821
822
823
824
825
826
827 @Override
828 public int getMethod() {
829 return method;
830 }
831
832
833
834
835
836
837
838
839
840
841 @Override
842 public String getName() {
843 return name == null ? super.getName() : name;
844 }
845
846
847
848
849
850
851
852 public NameSource getNameSource() {
853 return nameSource;
854 }
855
856 private ZipExtraField[] getParseableExtraFields() {
857 final ZipExtraField[] parseableExtraFields = getParseableExtraFieldsNoCopy();
858 return parseableExtraFields == extraFields ? copyOf(parseableExtraFields, parseableExtraFields.length) : parseableExtraFields;
859 }
860
861 private ZipExtraField[] getParseableExtraFieldsNoCopy() {
862 if (extraFields == null) {
863 return ExtraFieldUtils.EMPTY_ZIP_EXTRA_FIELD_ARRAY;
864 }
865 return extraFields;
866 }
867
868
869
870
871
872
873 public int getPlatform() {
874 return platform;
875 }
876
877
878
879
880
881
882
883 public int getRawFlag() {
884 return rawFlag;
885 }
886
887
888
889
890
891
892
893
894
895
896
897 public byte[] getRawName() {
898 if (rawName != null) {
899 return Arrays.copyOf(rawName, rawName.length);
900 }
901 return null;
902 }
903
904
905
906
907
908
909
910
911
912
913
914 @Override
915 public long getSize() {
916 return size;
917 }
918
919
920
921
922
923
924
925
926
927
928
929
930
931 @Override
932 public long getTime() {
933 if (lastModifiedDateSet) {
934 return getLastModifiedTime().toMillis();
935 }
936 return time != -1 ? time : super.getTime();
937 }
938
939
940
941
942
943
944 public int getUnixMode() {
945 return platform != PLATFORM_UNIX ? 0 : (int) (getExternalAttributes() >> SHORT_SHIFT & SHORT_MASK);
946 }
947
948
949
950
951
952
953
954
955 public UnparseableExtraFieldData getUnparseableExtraFieldData() {
956 return unparseableExtra;
957 }
958
959 private ZipExtraField[] getUnparseableOnly() {
960 return unparseableExtra == null ? ExtraFieldUtils.EMPTY_ZIP_EXTRA_FIELD_ARRAY : new ZipExtraField[] { unparseableExtra };
961 }
962
963
964
965
966
967
968
969 public int getVersionMadeBy() {
970 return versionMadeBy;
971 }
972
973
974
975
976
977
978
979 public int getVersionRequired() {
980 return versionRequired;
981 }
982
983
984
985
986
987
988 @Override
989 public int hashCode() {
990
991
992
993
994 return getName().hashCode();
995 }
996
997 private void internalAddExtraField(final ZipExtraField ze) {
998 if (ze instanceof UnparseableExtraFieldData) {
999 unparseableExtra = (UnparseableExtraFieldData) ze;
1000 } else if (extraFields == null) {
1001 extraFields = new ZipExtraField[] { ze };
1002 } else {
1003 if (getExtraField(ze.getHeaderId()) != null) {
1004 internalRemoveExtraField(ze.getHeaderId());
1005 }
1006 final ZipExtraField[] zipExtraFields = copyOf(extraFields, extraFields.length + 1);
1007 zipExtraFields[zipExtraFields.length - 1] = ze;
1008 extraFields = zipExtraFields;
1009 }
1010 }
1011
1012 private void internalRemoveExtraField(final ZipShort type) {
1013 if (extraFields == null) {
1014 return;
1015 }
1016 final List<ZipExtraField> newResult = new ArrayList<>();
1017 for (final ZipExtraField extraField : extraFields) {
1018 if (!type.equals(extraField.getHeaderId())) {
1019 newResult.add(extraField);
1020 }
1021 }
1022 if (extraFields.length == newResult.size()) {
1023 return;
1024 }
1025 extraFields = newResult.toArray(ExtraFieldUtils.EMPTY_ZIP_EXTRA_FIELD_ARRAY);
1026 }
1027
1028 private void internalSetLastModifiedTime(final FileTime time) {
1029 super.setLastModifiedTime(time);
1030 this.time = time.toMillis();
1031 lastModifiedDateSet = true;
1032 }
1033
1034
1035
1036
1037
1038
1039 @Override
1040 public boolean isDirectory() {
1041 return isDirectoryEntryName(getName());
1042 }
1043
1044 @Override
1045 public boolean isStreamContiguous() {
1046 return isStreamContiguous;
1047 }
1048
1049
1050
1051
1052
1053
1054
1055 public boolean isUnixSymlink() {
1056 return (getUnixMode() & UnixStat.FILE_TYPE_FLAG) == UnixStat.LINK_FLAG;
1057 }
1058
1059
1060
1061
1062
1063
1064
1065
1066 private void mergeExtraFields(final ZipExtraField[] f, final boolean local) {
1067 if (extraFields == null) {
1068 setExtraFields(f);
1069 } else {
1070 for (final ZipExtraField element : f) {
1071 final ZipExtraField existing;
1072 if (element instanceof UnparseableExtraFieldData) {
1073 existing = unparseableExtra;
1074 } else {
1075 existing = getExtraField(element.getHeaderId());
1076 }
1077 if (existing == null) {
1078 internalAddExtraField(element);
1079 } else {
1080 final byte[] b = local ? element.getLocalFileDataData() : element.getCentralDirectoryData();
1081 try {
1082 if (local) {
1083 existing.parseFromLocalFileData(b, 0, b.length);
1084 } else {
1085 existing.parseFromCentralDirectoryData(b, 0, b.length);
1086 }
1087 } catch (final ZipException ex) {
1088
1089 final UnrecognizedExtraField u = new UnrecognizedExtraField();
1090 u.setHeaderId(existing.getHeaderId());
1091 if (local) {
1092 u.setLocalFileDataData(b);
1093 u.setCentralDirectoryData(existing.getCentralDirectoryData());
1094 } else {
1095 u.setLocalFileDataData(existing.getLocalFileDataData());
1096 u.setCentralDirectoryData(b);
1097 }
1098 internalRemoveExtraField(existing.getHeaderId());
1099 internalAddExtraField(u);
1100 }
1101 }
1102 }
1103 setExtra();
1104 }
1105 }
1106
1107 private ZipExtraField[] parseExtraFields(final byte[] data, final boolean local, final ExtraFieldParsingBehavior parsingBehavior) throws ZipException {
1108 if (extraFieldFactory != null) {
1109 return ExtraFieldUtils.parse(data, local, new ExtraFieldParsingBehavior() {
1110 @Override
1111 public ZipExtraField createExtraField(final ZipShort headerId) throws ZipException, InstantiationException, IllegalAccessException {
1112 final ZipExtraField field = extraFieldFactory.apply(headerId);
1113 return field == null ? parsingBehavior.createExtraField(headerId) : field;
1114 }
1115
1116 @Override
1117 public ZipExtraField fill(final ZipExtraField field, final byte[] data, final int off, final int len, final boolean local) throws ZipException {
1118 return parsingBehavior.fill(field, data, off, len, local);
1119 }
1120
1121 @Override
1122 public ZipExtraField onUnparseableExtraField(final byte[] data, final int off, final int len, final boolean local, final int claimedLength)
1123 throws ZipException {
1124 return parsingBehavior.onUnparseableExtraField(data, off, len, local, claimedLength);
1125 }
1126 });
1127 }
1128 return ExtraFieldUtils.parse(data, local, parsingBehavior);
1129 }
1130
1131
1132
1133
1134
1135
1136 public void removeExtraField(final ZipShort type) {
1137 if (getExtraField(type) == null) {
1138 throw new NoSuchElementException();
1139 }
1140 internalRemoveExtraField(type);
1141 setExtra();
1142 }
1143
1144
1145
1146
1147
1148
1149 public void removeUnparseableExtraFieldData() {
1150 if (unparseableExtra == null) {
1151 throw new NoSuchElementException();
1152 }
1153 unparseableExtra = null;
1154 setExtra();
1155 }
1156
1157 private boolean requiresExtraTimeFields() {
1158 if (getLastAccessTime() != null || getCreationTime() != null) {
1159 return true;
1160 }
1161 return lastModifiedDateSet;
1162 }
1163
1164
1165
1166
1167
1168
1169
1170 public void setAlignment(final int alignment) {
1171 if ((alignment & alignment - 1) != 0 || alignment > 0xffff) {
1172 throw new IllegalArgumentException("Invalid value for alignment, must be power of two and no bigger than " + 0xffff + " but is " + alignment);
1173 }
1174 this.alignment = alignment;
1175 }
1176
1177 private void setAttributes(final Path inputPath, final LinkOption... options) throws IOException {
1178 final BasicFileAttributes attributes = Files.readAttributes(inputPath, BasicFileAttributes.class, options);
1179 if (attributes.isRegularFile()) {
1180 setSize(attributes.size());
1181 }
1182 super.setLastModifiedTime(attributes.lastModifiedTime());
1183 super.setCreationTime(attributes.creationTime());
1184 super.setLastAccessTime(attributes.lastAccessTime());
1185 setExtraTimeFields();
1186 }
1187
1188
1189
1190
1191
1192
1193 public void setCentralDirectoryExtra(final byte[] b) {
1194 try {
1195 mergeExtraFields(parseExtraFields(b, false, ExtraFieldParsingMode.BEST_EFFORT), false);
1196 } catch (final ZipException e) {
1197
1198 throw new IllegalArgumentException(e.getMessage(), e);
1199 }
1200 }
1201
1202
1203
1204
1205
1206
1207
1208 public void setCommentSource(final CommentSource commentSource) {
1209 this.commentSource = commentSource;
1210 }
1211
1212 @Override
1213 public ZipEntry setCreationTime(final FileTime time) {
1214 super.setCreationTime(time);
1215 setExtraTimeFields();
1216 return this;
1217 }
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229 protected void setDataOffset(final long dataOffset) {
1230 this.dataOffset = dataOffset;
1231 }
1232
1233
1234
1235
1236
1237
1238
1239 public void setDiskNumberStart(final long diskNumberStart) {
1240 this.diskNumberStart = diskNumberStart;
1241 }
1242
1243
1244
1245
1246
1247
1248 public void setExternalAttributes(final long value) {
1249 externalAttributes = value;
1250 }
1251
1252
1253
1254
1255
1256 protected void setExtra() {
1257
1258 super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getAllExtraFieldsNoCopy()));
1259
1260 updateTimeFieldsFromExtraFields();
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270 @Override
1271 public void setExtra(final byte[] extra) throws RuntimeException {
1272 try {
1273 mergeExtraFields(parseExtraFields(extra, true, ExtraFieldParsingMode.BEST_EFFORT), true);
1274 } catch (final ZipException e) {
1275
1276 throw new IllegalArgumentException("Error parsing extra fields for entry: "
1277 + getName() + " - " + e.getMessage(), e);
1278 }
1279 }
1280
1281
1282
1283
1284
1285
1286 public void setExtraFields(final ZipExtraField[] fields) {
1287 unparseableExtra = null;
1288 final List<ZipExtraField> newFields = new ArrayList<>();
1289 if (fields != null) {
1290 for (final ZipExtraField field : fields) {
1291 if (field instanceof UnparseableExtraFieldData) {
1292 unparseableExtra = (UnparseableExtraFieldData) field;
1293 } else {
1294 newFields.add(field);
1295 }
1296 }
1297 }
1298 extraFields = newFields.toArray(ExtraFieldUtils.EMPTY_ZIP_EXTRA_FIELD_ARRAY);
1299 setExtra();
1300 }
1301
1302 private void setExtraTimeFields() {
1303 if (getExtraField(X5455_ExtendedTimestamp.HEADER_ID) != null) {
1304 internalRemoveExtraField(X5455_ExtendedTimestamp.HEADER_ID);
1305 }
1306 if (getExtraField(X000A_NTFS.HEADER_ID) != null) {
1307 internalRemoveExtraField(X000A_NTFS.HEADER_ID);
1308 }
1309 if (requiresExtraTimeFields()) {
1310 final FileTime lastModifiedTime = getLastModifiedTime();
1311 final FileTime lastAccessTime = getLastAccessTime();
1312 final FileTime creationTime = getCreationTime();
1313 if (canConvertToInfoZipExtendedTimestamp(lastModifiedTime, lastAccessTime, creationTime)) {
1314 addInfoZipExtendedTimestamp(lastModifiedTime, lastAccessTime, creationTime);
1315 }
1316 addNTFSTimestamp(lastModifiedTime, lastAccessTime, creationTime);
1317 }
1318 setExtra();
1319 }
1320
1321
1322
1323
1324
1325
1326
1327 public void setGeneralPurposeBit(final GeneralPurposeBit generalPurposeBit) {
1328 this.generalPurposeBit = generalPurposeBit;
1329 }
1330
1331
1332
1333
1334
1335
1336 public void setInternalAttributes(final int internalAttributes) {
1337 this.internalAttributes = internalAttributes;
1338 }
1339
1340 @Override
1341 public ZipEntry setLastAccessTime(final FileTime fileTime) {
1342 super.setLastAccessTime(fileTime);
1343 setExtraTimeFields();
1344 return this;
1345 }
1346
1347 @Override
1348 public ZipEntry setLastModifiedTime(final FileTime fileTime) {
1349 internalSetLastModifiedTime(fileTime);
1350 setExtraTimeFields();
1351 return this;
1352 }
1353
1354 protected void setLocalHeaderOffset(final long localHeaderOffset) {
1355 this.localHeaderOffset = localHeaderOffset;
1356 }
1357
1358
1359
1360
1361
1362
1363
1364
1365 @Override
1366 public void setMethod(final int method) {
1367 if (method < 0) {
1368 throw new IllegalArgumentException("ZIP compression method can not be negative: " + method);
1369 }
1370 this.method = method;
1371 }
1372
1373
1374
1375
1376
1377
1378 protected void setName(String name) {
1379 if (name != null && getPlatform() == PLATFORM_FAT && !name.contains(ZIP_DIR_SEP)) {
1380 name = name.replace('\\', '/');
1381 }
1382 this.name = name;
1383 }
1384
1385
1386
1387
1388
1389
1390
1391
1392 protected void setName(final String name, final byte[] rawName) {
1393 setName(name);
1394 this.rawName = rawName;
1395 }
1396
1397
1398
1399
1400
1401
1402
1403 public void setNameSource(final NameSource nameSource) {
1404 this.nameSource = nameSource;
1405 }
1406
1407
1408
1409
1410
1411
1412 protected void setPlatform(final int platform) {
1413 this.platform = platform;
1414 }
1415
1416
1417
1418
1419
1420
1421
1422 public void setRawFlag(final int rawFlag) {
1423 this.rawFlag = rawFlag;
1424 }
1425
1426
1427
1428
1429
1430
1431
1432 @Override
1433 public void setSize(final long size) {
1434 if (size < 0) {
1435 throw new IllegalArgumentException("Invalid entry size");
1436 }
1437 this.size = size;
1438 }
1439
1440 protected void setStreamContiguous(final boolean isStreamContiguous) {
1441 this.isStreamContiguous = isStreamContiguous;
1442 }
1443
1444
1445
1446
1447
1448
1449
1450 public void setTime(final FileTime fileTime) {
1451 setTime(fileTime.toMillis());
1452 }
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 @Override
1467 public void setTime(final long timeEpochMillis) {
1468 if (ZipUtil.isDosTime(timeEpochMillis)) {
1469 super.setTime(timeEpochMillis);
1470 this.time = timeEpochMillis;
1471 lastModifiedDateSet = false;
1472 setExtraTimeFields();
1473 } else {
1474 setLastModifiedTime(FileTime.fromMillis(timeEpochMillis));
1475 }
1476 }
1477
1478
1479
1480
1481
1482
1483 public void setUnixMode(final int mode) {
1484
1485 setExternalAttributes(mode << SHORT_SHIFT
1486
1487 | ((mode & 0200) == 0 ? 1 : 0)
1488
1489 | (isDirectory() ? 0x10 : 0));
1490
1491 platform = PLATFORM_UNIX;
1492 }
1493
1494
1495
1496
1497
1498
1499
1500 public void setVersionMadeBy(final int versionMadeBy) {
1501 this.versionMadeBy = versionMadeBy;
1502 }
1503
1504
1505
1506
1507
1508
1509
1510 public void setVersionRequired(final int versionRequired) {
1511 this.versionRequired = versionRequired;
1512 }
1513
1514 private void updateTimeFieldsFromExtraFields() {
1515
1516 updateTimeFromExtendedTimestampField();
1517
1518 updateTimeFromNtfsField();
1519 }
1520
1521
1522
1523
1524
1525 private void updateTimeFromExtendedTimestampField() {
1526 final ZipExtraField extraField = getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
1527 if (extraField instanceof X5455_ExtendedTimestamp) {
1528 final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) extraField;
1529 if (extendedTimestamp.isBit0_modifyTimePresent()) {
1530 final FileTime modifyTime = extendedTimestamp.getModifyFileTime();
1531 if (modifyTime != null) {
1532 internalSetLastModifiedTime(modifyTime);
1533 }
1534 }
1535 if (extendedTimestamp.isBit1_accessTimePresent()) {
1536 final FileTime accessTime = extendedTimestamp.getAccessFileTime();
1537 if (accessTime != null) {
1538 super.setLastAccessTime(accessTime);
1539 }
1540 }
1541 if (extendedTimestamp.isBit2_createTimePresent()) {
1542 final FileTime creationTime = extendedTimestamp.getCreateFileTime();
1543 if (creationTime != null) {
1544 super.setCreationTime(creationTime);
1545 }
1546 }
1547 }
1548 }
1549
1550
1551
1552
1553
1554 private void updateTimeFromNtfsField() {
1555 final ZipExtraField extraField = getExtraField(X000A_NTFS.HEADER_ID);
1556 if (extraField instanceof X000A_NTFS) {
1557 final X000A_NTFS ntfsTimestamp = (X000A_NTFS) extraField;
1558 final FileTime modifyTime = ntfsTimestamp.getModifyFileTime();
1559 if (modifyTime != null) {
1560 internalSetLastModifiedTime(modifyTime);
1561 }
1562 final FileTime accessTime = ntfsTimestamp.getAccessFileTime();
1563 if (accessTime != null) {
1564 super.setLastAccessTime(accessTime);
1565 }
1566 final FileTime creationTime = ntfsTimestamp.getCreateFileTime();
1567 if (creationTime != null) {
1568 super.setCreationTime(creationTime);
1569 }
1570 }
1571 }
1572 }