1/*
2 * Copyright (c) 2021 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
16package ohos;
17
18import com.alibaba.fastjson.JSONObject;
19
20import java.io.IOException;
21import java.io.RandomAccessFile;
22import java.util.Optional;
23
24/**
25 * bin file to hap tool main class.
26 *
27 */
28public class CollectBinInfo {
29    private static final String JSON_FILE_NAME = "config.json";
30    private static final String PROFILE_KEY = "app";
31    private static final String VERSION_KEY = "version";
32    private static final String CODE_KEY = "code";
33    private static final String BIN_PATH = "--bin-path";
34    private static final String VERSION_CODE = "--version-code";
35    private static final int PARA_COUNT = 3;
36    private static final int MAGIC_NUMBER = 190;
37    private static final int INT_LENGTH = 4;
38    private static final int LONG_LENGTH = 8;
39    private static int bundleNameLen = 0;
40
41    /**
42     * get versionCode from bin file
43     *
44     * @param binPath: bin path
45     * @return versionCode
46     */
47    public static String getVersionCode(final String binPath) {
48        String versionCode = "";
49        if (!FileUtils.checkFileIsExists(binPath)) {
50            return versionCode;
51        }
52
53        Optional<String> absBinPath = FileUtils.getFormatedPath(binPath);
54        RandomAccessFile binStream = null;
55        try {
56            binStream = new RandomAccessFile(absBinPath.get(), "r");
57            if (!readBinFileHeader(binStream)) {
58                return versionCode;
59            }
60            long fileDataPos = 1 + INT_LENGTH + bundleNameLen;
61            byte [] fileDataByte = readBinFileData(JSON_FILE_NAME, binStream, fileDataPos);
62            if (fileDataByte == null) {
63                return versionCode;
64            }
65            String fileData = new String(fileDataByte, "UTF-8");
66            JSONObject object = JSONObject.parseObject(fileData);
67            if (object == null || !object.containsKey(PROFILE_KEY)) {
68                return versionCode;
69            }
70            object = object.getJSONObject(PROFILE_KEY);
71            if (object == null || !object.containsKey(VERSION_KEY)) {
72                return versionCode;
73            }
74            object = object.getJSONObject(VERSION_KEY);
75            if (object != null) {
76                versionCode = object.getInteger(CODE_KEY).toString();
77            }
78            return versionCode;
79        } catch (IOException msg) {
80            return versionCode;
81        } finally {
82            if (binStream != null) {
83                FileUtils.closeStream(binStream);
84            }
85        }
86    }
87
88    /**
89     * read bin file header
90     *
91     * @param binStream: bin randAccessFileStream
92     * @return true or false
93     */
94    private static boolean readBinFileHeader(RandomAccessFile binStream) {
95        try {
96            byte magic = binStream.readByte();
97            if ((magic & 0xff) != MAGIC_NUMBER) {
98                return false;
99            }
100            bundleNameLen = binStream.readInt();
101            if (bundleNameLen >= 0) {
102                byte [] bundleNameByte = new byte[bundleNameLen];
103                binStream.readFully(bundleNameByte, 0, bundleNameLen);
104                return true;
105            }
106            return false;
107        } catch (IOException msg) {
108            return false;
109        }
110    }
111
112    /**
113     * read bin file data
114     *
115     * @param fileName: file name
116     * @param binStream: bin randAccessFileStream
117     * @param fileDataPos: position which begin to read file data
118     * @return fileData byte
119     */
120    private static byte [] readBinFileData(final String fileName, RandomAccessFile binStream, long fileDataPos) {
121        try {
122            long fileSize = binStream.length();
123            long pos = fileDataPos;
124            while (pos < fileSize) {
125                int nameLen = binStream.readInt();
126                if (nameLen <= 0) {
127                    return null;
128                }
129                byte [] fileNameByte = new byte[nameLen];
130                binStream.readFully(fileNameByte, 0, nameLen);
131                int relativePathLen = binStream.readInt();
132                if (relativePathLen < 0) {
133                    return null;
134                }
135                binStream.seek(pos + INT_LENGTH + nameLen + INT_LENGTH + relativePathLen);
136                long dataLen = binStream.readLong();
137                if (dataLen > Integer.MAX_VALUE || dataLen <= 0) {
138                    return null;
139                }
140                byte [] fileData = new byte[(int) dataLen];
141                binStream.readFully(fileData, 0, (int) dataLen);
142                pos = pos + INT_LENGTH + nameLen + INT_LENGTH + relativePathLen + LONG_LENGTH + dataLen;
143                String name = new String(fileNameByte, "UTF-8");
144                if (name.equals(fileName)) {
145                    return fileData;
146                }
147            }
148            return null;
149        } catch (IOException msg) {
150            return null;
151        }
152    }
153
154    /**
155     * tool main function.
156     *
157     * @param args command line
158     */
159    public static void main(String[] args) {
160        if (args.length != PARA_COUNT) {
161            return;
162        }
163        String binPath = "";
164        int count = 0;
165        boolean versionFlag = false;
166        for (String para : args) {
167            if (BIN_PATH.equals(para) && count < args.length - 1) {
168                binPath = args[count + 1];
169            }
170            if (VERSION_CODE.equals(para) && count < args.length) {
171                versionFlag = true;
172            }
173            count++;
174        }
175        if (!binPath.isEmpty() && versionFlag) {
176            String versionCode = getVersionCode(binPath);
177        }
178    }
179}