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.codesigning.sign; 17 18 import com.ohos.hapsigntool.utils.FileUtils; 19 20 import org.bouncycastle.util.Strings; 21 22 import java.util.Locale; 23 import java.util.zip.ZipEntry; 24 25 /** 26 * Central directory structure 27 * further reference to <a herf="https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT">Zip Format</a> 28 * 29 * @since 2023/09/14 30 */ 31 public class CentralDirectory { 32 /** 33 * Byte size of all fields before "compression method" in central directory structure 34 */ 35 public static final int BYTE_SIZE_BEFORE_COMPRESSION_METHOD = 10; 36 37 /** 38 * Byte size of all fields between "compression method" and "file comment length" in central directory structure 39 */ 40 public static final int BYTE_SIZE_BETWEEN_COMPRESSION_MODE_AND_FILE_SIZE = 16; 41 42 /** 43 * Byte size of all fields between "file comment length" and 44 * "relative offset of local header" in central directory structure 45 */ 46 public static final int BYTE_SIZE_BETWEEN_FILE_COMMENT_LENGTH_AND_LOCHDR_RELATIVE_OFFSET = 8; 47 48 private final char compressionMethod; 49 50 private final char fileNameLength; 51 52 private final char extraFieldLength; 53 54 private final char fileCommentLength; 55 56 private final long relativeOffsetOfLocalHeader; 57 58 private final byte[] fileName; 59 CentralDirectory(Builder builder)60 public CentralDirectory(Builder builder) { 61 this.compressionMethod = builder.compressionMethod; 62 this.fileNameLength = builder.fileNameLength; 63 this.extraFieldLength = builder.extraFieldLength; 64 this.fileCommentLength = builder.fileCommentLength; 65 this.relativeOffsetOfLocalHeader = builder.relativeOffsetOfLocalHeader; 66 this.fileName = builder.fileName; 67 } 68 69 /** 70 * Return true if entry is an executable file, i.e. abc or so 71 * 72 * @return true if entry is an executable file 73 */ isCodeFile()74 public boolean isCodeFile() { 75 return FileUtils.isRunnableFile(this.getFileName()); 76 } 77 78 /** 79 * Return true if zip entry is uncompressed 80 * 81 * @return true if zip entry is uncompressed 82 */ isUncompressed()83 public boolean isUncompressed() { 84 return this.compressionMethod == ZipEntry.STORED; 85 } 86 getFileName()87 public String getFileName() { 88 return Strings.fromByteArray(this.fileName); 89 } 90 getRelativeOffsetOfLocalHeader()91 public long getRelativeOffsetOfLocalHeader() { 92 return relativeOffsetOfLocalHeader; 93 } 94 95 /** 96 * Sum byte size of three variable fields: file name, extra field, file comment 97 * 98 * @return Sum byte size of three variable fields 99 */ getFileNameLength()100 public char getFileNameLength() { 101 return fileNameLength; 102 } 103 getExtraFieldLength()104 public char getExtraFieldLength() { 105 return extraFieldLength; 106 } 107 108 /** 109 * Return a string representation of the object 110 * 111 * @return string representation of the object 112 */ toString()113 public String toString() { 114 return String.format(Locale.ROOT, 115 "CentralDirectory:compressionMode(%d), fileName(%s), relativeOffsetOfLocalHeader(%d), " 116 + "fileNameLength(%d), extraFieldLength(%d), fileCommentLength(%d)", (int) this.compressionMethod, 117 this.getFileName(), this.relativeOffsetOfLocalHeader, (int) this.fileNameLength, 118 (int) this.extraFieldLength, (int) this.fileCommentLength); 119 } 120 121 /** 122 * Builder of CentralDirectory class 123 */ 124 public static class Builder { 125 private char compressionMethod; 126 127 private char fileNameLength; 128 129 private char extraFieldLength; 130 131 private char fileCommentLength; 132 133 private long relativeOffsetOfLocalHeader; 134 135 private byte[] fileName; 136 setCompressionMethod(char compressionMethod)137 public Builder setCompressionMethod(char compressionMethod) { 138 this.compressionMethod = compressionMethod; 139 return this; 140 } 141 setFileNameLength(char fileNameLength)142 public Builder setFileNameLength(char fileNameLength) { 143 this.fileNameLength = fileNameLength; 144 return this; 145 } 146 setExtraFieldLength(char extraFieldLength)147 public Builder setExtraFieldLength(char extraFieldLength) { 148 this.extraFieldLength = extraFieldLength; 149 return this; 150 } 151 setFileCommentLength(char fileCommentLength)152 public Builder setFileCommentLength(char fileCommentLength) { 153 this.fileCommentLength = fileCommentLength; 154 return this; 155 } 156 setRelativeOffsetOfLocalHeader(long relativeOffsetOfLocalHeader)157 public Builder setRelativeOffsetOfLocalHeader(long relativeOffsetOfLocalHeader) { 158 this.relativeOffsetOfLocalHeader = relativeOffsetOfLocalHeader; 159 return this; 160 } 161 setFileName(byte[] fileName)162 public Builder setFileName(byte[] fileName) { 163 this.fileName = fileName; 164 return this; 165 } 166 167 /** 168 * Create a CentralDirectory object 169 * 170 * @return a CentralDirectory object 171 */ build()172 public CentralDirectory build() { 173 return new CentralDirectory(this); 174 } 175 } 176 } 177