1 /*
2 * Copyright (c) 2021-2025 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 "config_parser.h"
17 #include <iostream>
18 #include <regex>
19 #include "reference_parser.h"
20 #include "restool_errors.h"
21
22 namespace OHOS {
23 namespace Global {
24 namespace Restool {
25 using namespace std;
26 const map<string, ConfigParser::ModuleType> ConfigParser::MODULE_TYPES = {
27 { "har", ModuleType::HAR },
28 { "entry", ModuleType::ENTRY },
29 { "feature", ModuleType::FEATURE },
30 { "shared", ModuleType::SHARED }
31 };
32
33 const map<string, string> ConfigParser::JSON_STRING_IDS = {
34 { "icon", "^\\$media:" },
35 { "label", "^\\$string:" },
36 { "description", "^\\$string:" },
37 { "theme", "^\\$theme:" },
38 { "reason", "^\\$string:" },
39 { "startWindowIcon", "^\\$media:" },
40 { "startWindowBackground", "^\\$color:"},
41 { "resource", "^\\$[a-z]+:" },
42 { "extra", "^\\$[a-z]+:" },
43 { "fileContextMenu", "^\\$profile:" },
44 { "orientation", "^\\$string:" },
45 { "value", "^\\$string:" },
46 { "startWindow", "^\\$profile:" }
47 };
48
49 const map<string, string> ConfigParser::JSON_ARRAY_IDS = {
50 { "landscapeLayouts", "^\\$layout:" },
51 { "portraitLayouts", "^\\$layout:" }
52 };
53
54 bool ConfigParser::useModule_ = false;
55
ConfigParser()56 ConfigParser::ConfigParser()
57 : filePath_(""), packageName_(""), moduleName_(""), moduleType_(ModuleType::NONE),
58 abilityIconId_(0), abilityLabelId_(0), root_(nullptr)
59 {
60 }
61
ConfigParser(const string & filePath)62 ConfigParser::ConfigParser(const string &filePath)
63 : filePath_(filePath), packageName_(""), moduleName_(""), moduleType_(ModuleType::NONE),
64 abilityIconId_(0), abilityLabelId_(0), root_(nullptr)
65 {
66 }
67
~ConfigParser()68 ConfigParser::~ConfigParser()
69 {
70 if (root_) {
71 cJSON_Delete(root_);
72 }
73 }
74
Init()75 uint32_t ConfigParser::Init()
76 {
77 if (!ResourceUtil::OpenJsonFile(filePath_, &root_)) {
78 return RESTOOL_ERROR;
79 }
80
81 if (!root_ || !cJSON_IsObject(root_)) {
82 PrintError(GetError(ERR_CODE_JSON_FORMAT_ERROR).SetPosition(filePath_));
83 return RESTOOL_ERROR;
84 }
85
86 cJSON *appNode = cJSON_GetObjectItem(root_, "app");
87 if (appNode && cJSON_IsObject(appNode)) {
88 cJSON *minAPIVersionNode = cJSON_GetObjectItem(appNode, "minAPIVersion");
89 if (minAPIVersionNode && minAPIVersionNode->valueint >= MIN_SUPPORT_NEW_MODULE_API_VERSION) {
90 newModule_ = true;
91 }
92 }
93
94 cJSON *moduleNode = cJSON_GetObjectItem(root_, "module");
95 if (!ParseModule(moduleNode)) {
96 return RESTOOL_ERROR;
97 }
98 return RESTOOL_SUCCESS;
99 }
100
GetPackageName() const101 const string &ConfigParser::GetPackageName() const
102 {
103 return packageName_;
104 }
105
GetModuleName() const106 const string &ConfigParser::GetModuleName() const
107 {
108 return moduleName_;
109 }
110
GetAbilityIconId() const111 int64_t ConfigParser::GetAbilityIconId() const
112 {
113 return abilityIconId_;
114 }
115
GetAbilityLabelId() const116 int64_t ConfigParser::GetAbilityLabelId() const
117 {
118 return abilityLabelId_;
119 }
120
GetModuleType() const121 ConfigParser::ModuleType ConfigParser::GetModuleType() const
122 {
123 return moduleType_;
124 }
125
ParseRefence()126 uint32_t ConfigParser::ParseRefence()
127 {
128 if (ParseRefImpl(root_, "", root_)) {
129 return RESTOOL_SUCCESS;
130 }
131 return RESTOOL_ERROR;
132 }
133
Save(const string & filePath) const134 uint32_t ConfigParser::Save(const string &filePath) const
135 {
136 if (ResourceUtil::SaveToJsonFile(filePath, root_)) {
137 return RESTOOL_SUCCESS;
138 }
139 return RESTOOL_ERROR;
140 }
141
SetAppIcon(string & icon,int64_t id)142 bool ConfigParser::SetAppIcon(string &icon, int64_t id)
143 {
144 cJSON *appNode = cJSON_GetObjectItem(root_, "app");
145 if (!appNode || !cJSON_IsObject(appNode)) {
146 cerr << "Warning: 'app' not object" << endl;
147 return false;
148 }
149 cJSON_AddStringToObject(appNode, "icon", icon.c_str());
150 cJSON_AddNumberToObject(appNode, "iconId", id);
151 return true;
152 }
153
SetAppLabel(string & label,int64_t id)154 bool ConfigParser::SetAppLabel(string &label, int64_t id)
155 {
156 cJSON *appNode = cJSON_GetObjectItem(root_, "app");
157 if (!appNode || !cJSON_IsObject(appNode)) {
158 cerr << "Warning: 'app' not object" << endl;
159 return false;
160 }
161 cJSON_AddStringToObject(appNode, "label", label.c_str());
162 cJSON_AddNumberToObject(appNode, "labelId", id);
163 return true;
164 }
165
166 // below private
ParseModule(cJSON * moduleNode)167 bool ConfigParser::ParseModule(cJSON *moduleNode)
168 {
169 if (!moduleNode || !cJSON_IsObject(moduleNode)) {
170 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("module", "object").SetPosition(filePath_));
171 return false;
172 }
173 if (cJSON_GetArraySize(moduleNode) == 0) {
174 PrintError(GetError(ERR_CODE_JSON_NODE_EMPTY).FormatCause("module").SetPosition(filePath_));
175 return false;
176 }
177
178 if (!useModule_) {
179 cJSON *packageNode = cJSON_GetObjectItem(moduleNode, "package");
180 if (packageNode && cJSON_IsString(packageNode)) {
181 packageName_ = packageNode->valuestring;
182 }
183 cJSON *distroNode = cJSON_GetObjectItem(moduleNode, "distro");
184 if (!ParseDistro(distroNode)) {
185 return false;
186 }
187 return ParseAbilitiesForDepend(moduleNode);
188 }
189 cJSON *nameNode = cJSON_GetObjectItem(moduleNode, "name");
190 if (nameNode && cJSON_IsString(nameNode)) {
191 moduleName_ = nameNode->valuestring;
192 }
193
194 if (moduleName_.empty()) {
195 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("module.name").SetPosition(filePath_));
196 return false;
197 }
198 cJSON *typeNode = cJSON_GetObjectItem(moduleNode, "type");
199 if (typeNode && cJSON_IsString(typeNode) && !ParseModuleType(typeNode->valuestring)) {
200 return false;
201 }
202 return true;
203 }
204
ParseAbilitiesForDepend(cJSON * moduleNode)205 bool ConfigParser::ParseAbilitiesForDepend(cJSON *moduleNode)
206 {
207 if (!IsDependEntry()) {
208 return true;
209 }
210 cJSON *mainAbilityNode = cJSON_GetObjectItem(moduleNode, "mainAbility");
211 if (mainAbilityNode && cJSON_IsString(mainAbilityNode)) {
212 mainAbility_ = mainAbilityNode->valuestring;
213 if (mainAbility_[0] == '.') {
214 mainAbility_ = packageName_ + mainAbility_;
215 }
216 return ParseAbilities(cJSON_GetObjectItem(moduleNode, "abilities"));
217 }
218 return true;
219 }
220
ParseDistro(cJSON * distroNode)221 bool ConfigParser::ParseDistro(cJSON *distroNode)
222 {
223 if (!distroNode || !cJSON_IsObject(distroNode)) {
224 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("distro", "object").SetPosition(filePath_));
225 return false;
226 }
227 if (cJSON_GetArraySize(distroNode) == 0) {
228 PrintError(GetError(ERR_CODE_JSON_NODE_EMPTY).FormatCause("distro").SetPosition(filePath_));
229 return false;
230 }
231
232 cJSON *moduleNameNode = cJSON_GetObjectItem(distroNode, "moduleName");
233 if (moduleNameNode && cJSON_IsString(moduleNameNode)) {
234 moduleName_ = moduleNameNode->valuestring;
235 }
236
237 if (moduleName_.empty()) {
238 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("distro.moduleName").SetPosition(filePath_));
239 return false;
240 }
241
242 cJSON *moduleTypeNode = cJSON_GetObjectItem(distroNode, "moduleType");
243 if (moduleTypeNode && cJSON_IsString(moduleTypeNode) && !ParseModuleType(moduleTypeNode->valuestring)) {
244 return false;
245 }
246 return true;
247 }
248
ParseAbilities(const cJSON * abilities)249 bool ConfigParser::ParseAbilities(const cJSON *abilities)
250 {
251 if (!abilities || !cJSON_IsArray(abilities)) {
252 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("abilites", "array").SetPosition(filePath_));
253 return false;
254 }
255 if (cJSON_GetArraySize(abilities) == 0) {
256 return true;
257 }
258 bool isMainAbility = false;
259 for (cJSON *ability = abilities->child; ability; ability = ability->next) {
260 if (!ParseAbilitiy(ability, isMainAbility)) {
261 return false;
262 }
263 if (isMainAbility) {
264 break;
265 }
266 }
267 return true;
268 }
269
ParseAbilitiy(const cJSON * ability,bool & isMainAbility)270 bool ConfigParser::ParseAbilitiy(const cJSON *ability, bool &isMainAbility)
271 {
272 if (!ability || !cJSON_IsObject(ability)) {
273 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("ability", "object").SetPosition(filePath_));
274 return false;
275 }
276 if (cJSON_GetArraySize(ability) == 0) {
277 return true;
278 }
279 cJSON *nameNode = cJSON_GetObjectItem(ability, "name");
280 if (!nameNode || !cJSON_IsString(nameNode)) {
281 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause("ability.name", "object")
282 .SetPosition(filePath_));
283 return false;
284 }
285 string name = nameNode->valuestring;
286 if (name[0] == '.') {
287 name = packageName_ + name;
288 }
289 if (mainAbility_ != name && !IsMainAbility(cJSON_GetObjectItem(ability, "skills"))) {
290 return true;
291 }
292 cJSON *iconIdNode = cJSON_GetObjectItem(ability, "iconId");
293 if (iconIdNode && ResourceUtil::IsIntValue(iconIdNode)) {
294 abilityIconId_ = iconIdNode->valueint;
295 }
296 if (abilityIconId_ <= 0) {
297 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("ability.iconId").SetPosition(filePath_));
298 return false;
299 }
300 cJSON *labelIdNode = cJSON_GetObjectItem(ability, "labelId");
301 if (labelIdNode && ResourceUtil::IsIntValue(labelIdNode)) {
302 abilityLabelId_ = labelIdNode->valueint;
303 }
304 if (abilityLabelId_ <= 0) {
305 PrintError(GetError(ERR_CODE_JSON_NODE_MISSING).FormatCause("ability.labelId").SetPosition(filePath_));
306 return false;
307 }
308 isMainAbility = true;
309 return true;
310 }
311
IsMainAbility(const cJSON * skills)312 bool ConfigParser::IsMainAbility(const cJSON *skills)
313 {
314 if (!skills || !cJSON_IsArray(skills)) {
315 return false;
316 }
317 for (cJSON *skill = skills->child; skill; skill = skill->next) {
318 if (!cJSON_IsObject(skill)) {
319 return false;
320 }
321 if (IsHomeAction(cJSON_GetObjectItem(skill, "actions"))) {
322 return true;
323 }
324 }
325 return false;
326 }
327
IsHomeAction(const cJSON * actions)328 bool ConfigParser::IsHomeAction(const cJSON *actions)
329 {
330 if (!actions || !cJSON_IsArray(actions)) {
331 return false;
332 }
333 for (cJSON *action = actions->child; action; action = action->next) {
334 if (!cJSON_IsObject(action)) {
335 return false;
336 }
337 if (strcmp(action->valuestring, "action.system.home") == 0) {
338 return true;
339 }
340 }
341 return false;
342 }
343
ParseRefImpl(cJSON * parent,const string & key,cJSON * node)344 bool ConfigParser::ParseRefImpl(cJSON *parent, const string &key, cJSON *node)
345 {
346 if (cJSON_IsArray(node)) {
347 const auto &result = JSON_ARRAY_IDS.find(key);
348 if (result != JSON_ARRAY_IDS.end()) {
349 return ParseJsonArrayRef(parent, key, node);
350 }
351 cJSON *arrayItem = node->child;
352 while (arrayItem) {
353 if (!ParseRefImpl(node, "", arrayItem)) {
354 return false;
355 }
356 arrayItem = arrayItem->next;
357 }
358 } else if (cJSON_IsObject(node)) {
359 cJSON *child = node->child;
360 while (child) {
361 if (!ParseRefImpl(node, child->string, child)) {
362 return false;
363 }
364 child = child->next;
365 }
366 } else if (!key.empty() && cJSON_IsString(node)) {
367 return ParseJsonStringRef(parent, key, node);
368 }
369 return true;
370 }
371
ParseJsonArrayRef(cJSON * parent,const string & key,cJSON * node)372 bool ConfigParser::ParseJsonArrayRef(cJSON *parent, const string &key, cJSON *node)
373 {
374 if (!node || !cJSON_IsArray(node)) {
375 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(key.c_str(), "array")
376 .SetPosition(filePath_));
377 return false;
378 }
379 cJSON *array = cJSON_CreateArray();
380 for (cJSON *item = node->child; item; item = item->next) {
381 if (!cJSON_IsString(item)) {
382 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(string(key + " value").c_str(), "string")
383 .SetPosition(filePath_));
384 cJSON_Delete(array);
385 return false;
386 }
387 string value = item->valuestring;
388 bool update = false;
389 if (!GetRefIdFromString(value, update, JSON_ARRAY_IDS.at(key))) {
390 cJSON_Delete(array);
391 return false;
392 }
393 if (update) {
394 cJSON_AddItemToArray(array, cJSON_CreateNumber(atoll(value.c_str())));
395 }
396 }
397 cJSON_AddItemToObject(parent, (key + "Id").c_str(), array);
398 return true;
399 }
400
ParseJsonStringRef(cJSON * parent,const string & key,cJSON * node)401 bool ConfigParser::ParseJsonStringRef(cJSON *parent, const string &key, cJSON *node)
402 {
403 const auto &result = JSON_STRING_IDS.find(key);
404 if (result == JSON_STRING_IDS.end()) {
405 return true;
406 }
407 if (!node || !cJSON_IsString(node)) {
408 PrintError(GetError(ERR_CODE_JSON_NODE_MISMATCH).FormatCause(key.c_str(), "string").SetPosition(filePath_));
409 return false;
410 }
411 string value = node->valuestring;
412 bool update = false;
413 if (!GetRefIdFromString(value, update, JSON_STRING_IDS.at(key))) {
414 return false;
415 }
416 if (update) {
417 cJSON_AddItemToObject(parent, (key + "Id").c_str(), cJSON_CreateNumber(atoll(value.c_str())));
418 AddCheckNode(key, static_cast<uint32_t>(atoll(value.c_str())));
419 }
420 return true;
421 }
422
AddCheckNode(const string & key,uint32_t id)423 void ConfigParser::AddCheckNode(const string &key, uint32_t id)
424 {
425 if (g_keyNodeIndexs.find(key) != g_keyNodeIndexs.end()) {
426 auto result = jsonCheckIds_.find(key);
427 if (result == jsonCheckIds_.end()) {
428 set<uint32_t> set;
429 set.emplace(id);
430 jsonCheckIds_.emplace(key, set);
431 } else {
432 result->second.emplace(id);
433 }
434 auto layerIconIds = ReferenceParser::GetLayerIconIds();
435 if (layerIconIds.find(id) != layerIconIds.end()) {
436 auto ids = layerIconIds[id];
437 jsonCheckIds_[key].insert(ids.begin(), ids.end());
438 }
439 }
440 }
441
GetRefIdFromString(string & value,bool & update,const string & match) const442 bool ConfigParser::GetRefIdFromString(string &value, bool &update, const string &match) const
443 {
444 ReferenceParser refParser;
445 if (refParser.ParseRefInString(value, update, filePath_) != RESTOOL_SUCCESS) {
446 return false;
447 }
448 if (!update) {
449 return true;
450 }
451 smatch result;
452 if (regex_search(value, result, regex(match))) {
453 value = value.substr(result[0].str().length());
454 return true;
455 }
456 string ref = match.substr(match.find("\\") + 1);
457 PrintError(GetError(ERR_CODE_INVALID_RESOURCE_REF).FormatCause(value.c_str(), ref.c_str()).SetPosition(filePath_));
458 return false;
459 }
460
ParseModuleType(const string & type)461 bool ConfigParser::ParseModuleType(const string &type)
462 {
463 const auto &result = MODULE_TYPES.find(type);
464 if (result == MODULE_TYPES.end()) {
465 PrintError(GetError(ERR_CODE_INVALID_MODULE_TYPE).FormatCause(type.c_str()).SetPosition(filePath_));
466 return false;
467 }
468 moduleType_ = result->second;
469 return true;
470 }
471 }
472 }
473 }
474