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