• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.ohos.hapsigntool.zip;
17 
18 import com.ohos.hapsigntool.error.ZipException;
19 
20 import java.util.Arrays;
21 import java.util.Calendar;
22 import java.util.zip.CRC32;
23 
24 /**
25  * ZipEntry and CentralDirectory data
26  *
27  * @since 2023/12/02
28  */
29 public class ZipEntry {
30     private ZipEntryData zipEntryData;
31 
32     private CentralDirectory fileEntryInCentralDirectory;
33 
34     /**
35      * updateLength
36      */
updateLength()37     public void updateLength() {
38         zipEntryData.updateLength();
39         fileEntryInCentralDirectory.updateLength();
40     }
41 
42     /**
43      * alignment one entry
44      *
45      * @param alignNum  need align bytes length
46      * @return add bytes length
47      * @throws ZipException alignment exception
48      */
alignment(int alignNum)49     public int alignment(int alignNum) throws ZipException {
50         // if cd extra len bigger than entry extra len, make cd and entry extra length equals
51         int padding = calZeroPaddingLengthForEntryExtra();
52         int remainder = (int) ((zipEntryData.getZipEntryHeader().getLength()
53                 + fileEntryInCentralDirectory.getOffset()) % alignNum);
54 
55         if (remainder == 0) {
56             return padding;
57         }
58         int add = alignNum - remainder;
59         int newExtraLength = zipEntryData.getZipEntryHeader().getExtraLength() + add;
60         if (newExtraLength > UnsignedDecimalUtil.MAX_UNSIGNED_SHORT_VALUE) {
61             throw new ZipException("can not align " + zipEntryData.getZipEntryHeader().getFileName());
62         }
63         setEntryHeaderNewExtraLength(newExtraLength);
64         setCenterDirectoryNewExtraLength(newExtraLength);
65 
66         return add;
67     }
68 
calZeroPaddingLengthForEntryExtra()69     private int calZeroPaddingLengthForEntryExtra() throws ZipException {
70         int entryExtraLen = zipEntryData.getZipEntryHeader().getExtraLength();
71         int cdExtraLen = fileEntryInCentralDirectory.getExtraLength();
72         if (cdExtraLen > entryExtraLen) {
73             setEntryHeaderNewExtraLength(cdExtraLen);
74             return cdExtraLen - entryExtraLen;
75         }
76         if (cdExtraLen < entryExtraLen) {
77             setCenterDirectoryNewExtraLength(entryExtraLen);
78             return entryExtraLen - cdExtraLen;
79         }
80         return 0;
81     }
82 
setCenterDirectoryNewExtraLength(int newLength)83     private void setCenterDirectoryNewExtraLength(int newLength) throws ZipException {
84         byte[] newCDExtra = getAlignmentNewExtra(newLength, fileEntryInCentralDirectory.getExtraData());
85         fileEntryInCentralDirectory.setExtraData(newCDExtra);
86         fileEntryInCentralDirectory.setExtraLength(newLength);
87         fileEntryInCentralDirectory.setLength(CentralDirectory.CD_LENGTH
88                 + fileEntryInCentralDirectory.getFileNameLength()
89                 + fileEntryInCentralDirectory.getExtraLength() + fileEntryInCentralDirectory.getCommentLength());
90     }
91 
setEntryHeaderNewExtraLength(int newLength)92     private void setEntryHeaderNewExtraLength(int newLength) throws ZipException {
93         ZipEntryHeader zipEntryHeader = zipEntryData.getZipEntryHeader();
94         byte[] newExtra = getAlignmentNewExtra(newLength, zipEntryHeader.getExtraData());
95         zipEntryHeader.setExtraData(newExtra);
96         zipEntryHeader.setExtraLength(newLength);
97         zipEntryHeader.setLength(ZipEntryHeader.HEADER_LENGTH + zipEntryHeader.getExtraLength()
98                 + zipEntryHeader.getFileNameLength());
99         zipEntryData.setLength(zipEntryHeader.getLength() + zipEntryData.getFileSize()
100                 + (zipEntryData.getDataDescriptor() == null ? 0 : DataDescriptor.DES_LENGTH));
101     }
102 
getAlignmentNewExtra(int newLength, byte[] old)103     private byte[] getAlignmentNewExtra(int newLength, byte[] old) throws ZipException {
104         if (old == null) {
105             return new byte[newLength];
106         }
107         if (newLength < old.length) {
108             throw new ZipException("can not align " + zipEntryData.getZipEntryHeader().getFileName());
109         }
110         return Arrays.copyOf(old, newLength);
111     }
112 
getZipEntryData()113     public ZipEntryData getZipEntryData() {
114         return zipEntryData;
115     }
116 
setZipEntryData(ZipEntryData zipEntryData)117     public void setZipEntryData(ZipEntryData zipEntryData) {
118         this.zipEntryData = zipEntryData;
119     }
120 
getCentralDirectory()121     public CentralDirectory getCentralDirectory() {
122         return fileEntryInCentralDirectory;
123     }
124 
setCentralDirectory(CentralDirectory centralDirectory)125     public void setCentralDirectory(CentralDirectory centralDirectory) {
126         this.fileEntryInCentralDirectory = centralDirectory;
127     }
128 
129     /**
130      * zip entry builder
131      */
132     public static class Builder {
133         private short version = 10;
134 
135         private short flag = 2048;
136 
137         private short method = 0;
138 
139         private long compressedSize;
140 
141         private long unCompressedSize;
142 
143         private String fileName;
144 
145         private byte[] extraData;
146 
147         private byte[] comment;
148 
149         private byte[] data;
150 
setVersion(short version)151         public Builder setVersion(short version) {
152             this.version = version;
153             return this;
154         }
155 
setFlag(short flag)156         public Builder setFlag(short flag) {
157             this.flag = flag;
158             return this;
159         }
160 
setMethod(short method)161         public Builder setMethod(short method) {
162             this.method = method;
163             return this;
164         }
165 
setCompressedSize(long compressedSize)166         public Builder setCompressedSize(long compressedSize) {
167             this.compressedSize = compressedSize;
168             return this;
169         }
170 
setUncompressedSize(long unCompressedSize)171         public Builder setUncompressedSize(long unCompressedSize) {
172             this.unCompressedSize = unCompressedSize;
173             return this;
174         }
175 
setFileName(String fileName)176         public Builder setFileName(String fileName) {
177             this.fileName = fileName;
178             return this;
179         }
180 
setExtraData(byte[] extraData)181         public Builder setExtraData(byte[] extraData) {
182             this.extraData = extraData;
183             return this;
184         }
185 
setComment(byte[] comment)186         public Builder setComment(byte[] comment) {
187             this.comment = comment;
188             return this;
189         }
190 
setData(byte[] data)191         public Builder setData(byte[] data) {
192             this.data = data;
193             return this;
194         }
195 
196         /**
197          * build zip entry
198          *
199          * @return Zip Entry
200          * @throws ZipException ZipException
201          */
build()202         public ZipEntry build() throws ZipException {
203             Calendar calendar = Calendar.getInstance();
204             // java time stamp to dos timestamp, dos time start 1980
205             int time = (calendar.get(Calendar.YEAR) - 1980) << 25 | (calendar.get(Calendar.MONTH) + 1) << 21
206                     | calendar.get(Calendar.DAY_OF_MONTH) << 16 | calendar.get(Calendar.HOUR_OF_DAY) << 11
207                     | calendar.get(Calendar.MINUTE) << 5 | calendar.get(Calendar.SECOND) >> 1;
208             CentralDirectory cd = addCenterDirectory(time);
209             ZipEntryHeader zipEntryHeader = addZipEntryHeader(time);
210             if (data == null) {
211                 throw new ZipException("can not find entry data");
212             }
213             final CRC32 c = new CRC32();
214             c.update(data);
215             final int crc32 = new Long(c.getValue()).intValue();
216             cd.setCrc32(crc32);
217             zipEntryHeader.setCrc32(crc32);
218 
219             ZipEntryData entryData = new ZipEntryData();
220             entryData.setData(data);
221             entryData.setZipEntryHeader(zipEntryHeader);
222             ZipEntry entry = new ZipEntry();
223             entry.setZipEntryData(entryData);
224             entryData.setType(EntryType.BIT_MAP);
225             entry.setCentralDirectory(cd);
226             return entry;
227         }
228 
addCenterDirectory(int time)229         private CentralDirectory addCenterDirectory(int time) {
230             CentralDirectory cd = new CentralDirectory();
231             cd.setVersion(version);
232             cd.setVersionExtra(version);
233             cd.setFlag(flag);
234             cd.setMethod(method);
235             cd.setLastTime((short) time);
236             cd.setLastDate((short) (time >> 16));
237             cd.setCompressedSize(compressedSize);
238             cd.setUnCompressedSize(unCompressedSize);
239             cd.setFileName(fileName);
240             cd.setFileNameLength(fileName.length());
241             if (extraData != null) {
242                 cd.setExtraData(extraData);
243                 cd.setExtraLength(extraData.length);
244             } else {
245                 cd.setExtraLength(0);
246             }
247             if (comment != null) {
248                 cd.setComment(comment);
249                 cd.setCommentLength(comment.length);
250             } else {
251                 cd.setCommentLength(0);
252             }
253             cd.setDiskNumStart(0);
254             cd.setExternalFile(0);
255 
256             cd.updateLength();
257             return cd;
258         }
259 
addZipEntryHeader(int time)260         private ZipEntryHeader addZipEntryHeader(int time) {
261             ZipEntryHeader zipEntryHeader = new ZipEntryHeader();
262             zipEntryHeader.setVersion(version);
263             zipEntryHeader.setFlag(flag);
264             zipEntryHeader.setMethod(method);
265             zipEntryHeader.setLastTime((short) time);
266             zipEntryHeader.setLastDate((short) (time >> 16));
267             zipEntryHeader.setCompressedSize(compressedSize);
268             zipEntryHeader.setUnCompressedSize(unCompressedSize);
269             zipEntryHeader.setFileName(fileName);
270             zipEntryHeader.setFileNameLength(fileName.length());
271             if (extraData != null) {
272                 zipEntryHeader.setExtraData(extraData);
273                 zipEntryHeader.setExtraLength(extraData.length);
274             } else {
275                 zipEntryHeader.setExtraLength(0);
276             }
277             zipEntryHeader.updateLength();
278             return zipEntryHeader;
279         }
280     }
281 }