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