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