1 /*
2 * Copyright (c) 2024 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 #include "packager.h"
17
18 #include <filesystem>
19 #include <fstream>
20 #include <random>
21 #include <sstream>
22 #include <string>
23 #include <unordered_set>
24
25 #include "json/json_utils.h"
26 #include "log.h"
27 #include "utils.h"
28 #include "zip_utils.h"
29
30 namespace OHOS {
31 namespace AppPackingTool {
32 namespace {
33 const std::string EN_US_UTF_8 = "en_US.UTF-8";
34 }
35
Packager(const std::map<std::string,std::string> & parameterMap,std::string & resultReceiver)36 Packager::Packager(const std::map<std::string, std::string> ¶meterMap, std::string &resultReceiver)
37 : parameterMap_(parameterMap), resultReceiver_(resultReceiver)
38 {}
39
~Packager()40 Packager::~Packager()
41 {}
42
MakePackage()43 int32_t Packager::MakePackage()
44 {
45 int32_t ret = ERR_OK;
46 ret = InitAllowedParam();
47 if (ret != ERR_OK) {
48 LOGE("InitAllowedParam err");
49 return ret;
50 }
51
52 ret = PreProcess();
53 if (ret != ERR_OK) {
54 LOGE("PreProcess err");
55 return ret;
56 }
57 ret = Process();
58 if (ret != ERR_OK) {
59 LOGE("Process err");
60 return ret;
61 }
62
63 ret = PostProcess();
64 if (ret != ERR_OK) {
65 LOGE("PostProcess err");
66 return ret;
67 }
68 return ret;
69 }
70
PreProcess()71 int32_t Packager::PreProcess()
72 {
73 resultReceiver_.append("do PreProcess\n");
74 return ERR_OK;
75 }
76
Process()77 int32_t Packager::Process()
78 {
79 resultReceiver_.append("do Process\n");
80 return ERR_OK;
81 }
82
PostProcess()83 int32_t Packager::PostProcess()
84 {
85 resultReceiver_.append("do PostProcess\n");
86 return ERR_OK;
87 }
88
CheckForceFlag()89 bool Packager::CheckForceFlag()
90 {
91 auto it = parameterMap_.find(Constants::PARAM_FORCE);
92 if (it != parameterMap_.end() && it->second != "false" && it->second != "true") {
93 LOGE("Packager::commandVerify forceRewrite is invalid.");
94 return false;
95 }
96 return true;
97 }
98
IsPathValid(const std::string & path,const bool & isFile,const std::string suffix)99 bool Packager::IsPathValid(const std::string &path, const bool &isFile, const std::string suffix)
100 {
101 if (isFile && fs::is_regular_file(path)) {
102 std::string name = fs::path(path).filename();
103 std::locale englishLocale(EN_US_UTF_8);
104 std::transform(name.begin(), name.end(), name.begin(),
105 [&englishLocale](unsigned char c) { return std::tolower(c); });
106 if (Utils::EndsWith(name, suffix)) {
107 return true;
108 }
109 }
110 return (!isFile) && fs::is_directory(path);
111 }
112
SplitDirList(const std::string & dirList,std::list<std::string> & fileList)113 bool Packager::SplitDirList(const std::string &dirList, std::list<std::string> &fileList)
114 {
115 std::list<std::string> pathList;
116 RemoveDuplicatePath(dirList, pathList);
117 for (const std::string &pathItem : pathList) {
118 std::string formattedPathItem;
119 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
120 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
121 return false;
122 };
123 if (!IsPathValid(formattedPathItem, false)) {
124 return false;
125 }
126 fileList.push_back(formattedPathItem);
127 }
128 return true;
129 }
130
RemoveDuplicatePath(const std::string & path,std::list<std::string> & pathList)131 void Packager::RemoveDuplicatePath(const std::string &path, std::list<std::string> &pathList)
132 {
133 std::string pathItem;
134 std::istringstream pathStream(path);
135 while (std::getline(pathStream, pathItem, Constants::COMMA_SPLIT)) {
136 pathList.push_back(pathItem);
137 }
138 std::unordered_set<std::string> uniqueTokens(pathList.begin(), pathList.end());
139 pathList.assign(uniqueTokens.begin(), uniqueTokens.end());
140 }
141
CompatibleProcess(const std::string & inputPath,std::list<std::string> & fileList,const std::string & suffix)142 bool Packager::CompatibleProcess(const std::string &inputPath, std::list<std::string> &fileList,
143 const std::string &suffix)
144 {
145 if (IsPathValid(inputPath, false)) {
146 for (const auto& fileItem : fs::recursive_directory_iterator(inputPath)) {
147 std::string name = fileItem.path().filename();
148 std::locale englishLocale(EN_US_UTF_8);
149 std::transform(name.begin(), name.end(), name.begin(),
150 [&englishLocale](unsigned char c) { return std::tolower(c); });
151 if (Utils::EndsWith(name, suffix)) {
152 fileList.push_back(fileItem.path().string());
153 }
154 }
155 return true;
156 } else {
157 std::string formattedPathItem;
158 std::list<std::string> pathList;
159 RemoveDuplicatePath(inputPath, pathList);
160 for (std::string pathItem : pathList) {
161 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
162 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
163 return false;
164 };
165 if (!IsPathValid(formattedPathItem, true, suffix)) {
166 return false;
167 }
168 fileList.push_back(formattedPathItem);
169 }
170 return true;
171 }
172 }
173
CompatibleProcess(const std::string & inputPath,std::list<std::string> & fileList,const std::string & suffix,const std::string & extraSuffix)174 bool Packager::CompatibleProcess(const std::string &inputPath, std::list<std::string> &fileList,
175 const std::string &suffix, const std::string &extraSuffix)
176 {
177 if (IsPathValid(inputPath, false)) {
178 for (const auto& fileItem : fs::recursive_directory_iterator(inputPath)) {
179 std::string name = fileItem.path().filename();
180 std::locale englishLocale(EN_US_UTF_8);
181 std::transform(name.begin(), name.end(), name.begin(),
182 [&englishLocale](unsigned char c) { return std::tolower(c); });
183 if (Utils::EndsWith(name, suffix) || Utils::EndsWith(name, extraSuffix)) {
184 fileList.push_back(fileItem.path().string());
185 }
186 }
187 return true;
188 } else {
189 std::string formattedPathItem;
190 std::list<std::string> pathList;
191 RemoveDuplicatePath(inputPath, pathList);
192 for (std::string pathItem : pathList) {
193 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
194 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
195 return false;
196 };
197 if (!IsPathValid(formattedPathItem, true, suffix) &&
198 !IsPathValid(formattedPathItem, true, extraSuffix)) {
199 return false;
200 }
201 fileList.push_back(formattedPathItem);
202 }
203 return true;
204 }
205 }
206
IsOutPathValid(const std::string & outPath,const std::string & forceRewrite,const std::string & suffix)207 bool Packager::IsOutPathValid(const std::string &outPath, const std::string &forceRewrite, const std::string &suffix)
208 {
209 fs::path filePath(outPath);
210
211 if ("false" == forceRewrite && fs::exists(filePath)) {
212 LOGE("Packager::isOutPathValid out file already existed.");
213 return false;
214 }
215
216 if (suffix == Constants::HAP_SUFFIX) {
217 if (!Utils::EndsWith(filePath.filename().string(), Constants::HAP_SUFFIX)) {
218 LOGE("Packager::isOutPathValid out-path must end with .hap.");
219 return false;
220 } else {
221 return true;
222 }
223 } else if (suffix == Constants::HAR_SUFFIX) {
224 if (!Utils::EndsWith(filePath.filename().string(), Constants::HAR_SUFFIX)) {
225 LOGE("Packager::isOutPathValid out-path must end with .har.");
226 return false;
227 } else {
228 return true;
229 }
230 } else if (suffix == Constants::APP_SUFFIX) {
231 if (!Utils::EndsWith(filePath.filename().string(), Constants::APP_SUFFIX)) {
232 LOGE("Packager::isOutPathValid out-path must end with .app.");
233 return false;
234 } else {
235 return true;
236 }
237 } else if (suffix == Constants::RES_SUFFIX) {
238 if (!Utils::EndsWith(filePath.filename().string(), Constants::RES_SUFFIX)) {
239 LOGE("Packager::isOutPathValid out-path must end with .res.");
240 return false;
241 } else {
242 return true;
243 }
244 } else if (suffix == Constants::HSP_SUFFIX) {
245 if (!Utils::EndsWith(filePath.filename().string(), Constants::HSP_SUFFIX)) {
246 LOGE("Packager::isOutPathValid out-path must end with .hsp.");
247 return false;
248 } else {
249 return true;
250 }
251 }
252 return false;
253 }
254
SetGenerateBuildHash(std::string & jsonPath,bool & generateBuildHash,bool & buildHashFinish)255 bool Packager::SetGenerateBuildHash(std::string &jsonPath, bool &generateBuildHash, bool &buildHashFinish)
256 {
257 if (!fs::exists(jsonPath)) {
258 LOGE("Packager::setGenerateBuildHash failed for json file not exist");
259 return false;
260 }
261 ModuleJson moduleJson;
262 moduleJson.ParseFromFile(jsonPath);
263
264 if (buildHashFinish || !moduleJson.GetGenerateBuildHash(generateBuildHash)) {
265 return true;
266 }
267
268 if (!CopyFileToTempDir(jsonPath)) {
269 return false;
270 }
271
272 if (!fs::exists(jsonPath)) {
273 LOGE("Packager::setGenerateBuildHash failed for json file not exist");
274 return false;
275 }
276
277 ModuleJson moduleJsonTemp;
278 moduleJsonTemp.ParseFromFile(jsonPath);
279 if (!moduleJsonTemp.GetGenerateBuildHash(generateBuildHash)) {
280 LOGE("ModuleJson::GetGenerateBuildHash failed");
281 return false;
282 }
283
284 if (!moduleJsonTemp.RemoveGenerateBuildHash()) {
285 LOGE("ModuleJson::RemoveGenerateBuildHash failed");
286 return false;
287 }
288
289 std::string prettyJsonString = moduleJsonTemp.ToString();
290 if (prettyJsonString.empty()) {
291 LOGE("ModuleJson::ToString failed");
292 return false;
293 }
294
295 std::string realJsonPath;
296 if (!Utils::GetRealPath(jsonPath, realJsonPath)) {
297 LOGE("get real json Path failed! jsonPath=%s", jsonPath.c_str());
298 return false;
299 }
300 std::ofstream outFile(realJsonPath);
301 if (outFile.is_open()) {
302 outFile << prettyJsonString.c_str();
303 outFile.close();
304 } else {
305 LOGE("Failed to open file for writing");
306 return false;
307 }
308 return true;
309 }
310
CopyFileToTempDir(std::string & jsonPath)311 bool Packager::CopyFileToTempDir(std::string &jsonPath)
312 {
313 if (!fs::exists(jsonPath)) {
314 LOGE("Packager::copyFileToTempDir failed for json file not found.");
315 return false;
316 }
317 fs::path oldFileParent = fs::path(jsonPath).parent_path();
318 std::string tempDir = Constants::COMPRESSOR_TEMP_DIR + fs::path::preferred_separator + Utils::GenerateUUID();
319 fs::path tempDirFs(tempDir);
320 tempDir = oldFileParent.string() + fs::path::preferred_separator + tempDirFs.string();
321 fs::create_directories(tempDir);
322 fs::path fileName = JsonUtils::IsModuleJson(jsonPath) ? Constants::MODULE_JSON : Constants::CONFIG_JSON;
323 std::string tempPath = tempDir + fs::path::preferred_separator + fileName.string();
324 if (!Utils::CopyFile(jsonPath, tempPath)) {
325 return false;
326 }
327 jsonPath = tempPath;
328 return true;
329 }
330
BuildHash(bool & buildHashFinish,const bool & generateBuildHash,const std::map<std::string,std::string> & parameterMap,const std::string & jsonPath)331 bool Packager::BuildHash(bool &buildHashFinish, const bool &generateBuildHash,
332 const std::map<std::string, std::string> ¶meterMap, const std::string &jsonPath)
333 {
334 if (buildHashFinish || !generateBuildHash) {
335 return true;
336 }
337 std::map<std::string, std::string>::const_iterator it = parameterMap.find(Constants::PARAM_OUT_PATH);
338 if (it == parameterMap.end()) {
339 LOGE("out-path not found");
340 return false;
341 }
342 std::string filePath = it->second;
343 std::string hash = Utils::GetSha256File(filePath);
344 return PutBuildHash(jsonPath, hash, buildHashFinish);
345 }
346
PutBuildHash(const std::string & jsonPath,const std::string & hash,bool & buildHashFinish)347 bool Packager::PutBuildHash(const std::string &jsonPath, const std::string &hash, bool &buildHashFinish)
348 {
349 if (buildHashFinish) {
350 return true;
351 }
352
353 ModuleJson moduleJson;
354 moduleJson.ParseFromFile(jsonPath);
355 moduleJson.SetBuildHash(hash);
356 std::string prettyJsonString = moduleJson.ToString();
357 if (prettyJsonString.empty()) {
358 LOGE("ModuleJson::ToString failed");
359 return false;
360 }
361
362 std::string realJsonPath;
363 if (!Utils::GetRealPath(jsonPath, realJsonPath)) {
364 LOGE("get real json path failed! jsonFile=%s", jsonPath.c_str());
365 return false;
366 }
367 std::ofstream outFile(realJsonPath);
368 if (outFile.is_open()) {
369 outFile << prettyJsonString.c_str();
370 outFile.close();
371 } else {
372 LOGE("Failed to open file for writing");
373 return false;
374 }
375
376 buildHashFinish = true;
377 return true;
378 }
379
IsModuleHap(const std::string & hapPath)380 bool Packager::IsModuleHap(const std::string& hapPath)
381 {
382 if (!Utils::EndsWith(hapPath, Constants::HAP_SUFFIX)) {
383 return false;
384 }
385 if (!ZipUtils::IsFileExistsInZip(hapPath, Constants::MODULE_JSON)) {
386 return false;
387 }
388 return true;
389 }
390
CompressPackinfoIntoHap(const std::string & hapPathItem,const std::string & unzipPathString,const std::string & outPathString,const std::string & packInfoPath)391 void Packager::CompressPackinfoIntoHap(const std::string& hapPathItem, const std::string& unzipPathString,
392 const std::string& outPathString, const std::string& packInfoPath)
393 {
394 if (!fs::exists(fs::path(unzipPathString))) {
395 fs::create_directory(fs::path(unzipPathString));
396 }
397 ZipUtils::Unzip(hapPathItem, unzipPathString);
398 for (const auto& entry : fs::directory_iterator(unzipPathString)) {
399 if (entry.path().filename() == Constants::PACK_INFO) {
400 fs::remove(entry.path());
401 }
402 }
403
404 std::string realPackInfoPath;
405 if (!Utils::GetRealPath(packInfoPath, realPackInfoPath)) {
406 LOGE("get real pack info path failed! packInfoPath=%s", packInfoPath.c_str());
407 return;
408 }
409 std::string destFilePath = unzipPathString + fs::path::preferred_separator + Constants::PACK_INFO;
410 std::string realDestFilePath;
411 if (!Utils::GetRealPathOfNoneExistFile(destFilePath, realDestFilePath)) {
412 LOGE("get real dest file path failed! destFilePath=%s", destFilePath.c_str());
413 return;
414 }
415 std::ifstream packInfoFile(realPackInfoPath, std::ios::binary);
416 std::ofstream destFile(realDestFilePath, std::ios::binary | std::ios::trunc);
417 destFile << packInfoFile.rdbuf();
418 destFile.close();
419 packInfoFile.close();
420 ZipUtils::Zip(unzipPathString, outPathString);
421 if (fs::exists(fs::path(unzipPathString))) {
422 fs::remove_all(fs::path(unzipPathString));
423 }
424 }
425
IsOutDirectoryValid()426 bool Packager::IsOutDirectoryValid()
427 {
428 auto it = parameterMap_.find(Constants::PARAM_OUT_PATH);
429 if (it == parameterMap_.end()) {
430 LOGE("Validate out-path is empty");
431 return false;
432 } else if (!Utils::IsDirectory(it->second)) {
433 LOGE("Validate out-path is not a directory.");
434 return false;
435 }
436 return true;
437 }
438 } // namespace AppPackingTool
439 } // namespace OHOS