1 /*
2 * Copyright (c) 2023 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 #define LOG_TAG "UnifiedKey"
16 #include <sstream>
17 #include "unified_key.h"
18
19 #include "logger.h"
20
21 namespace OHOS {
22 namespace UDMF {
23 static std::bitset<MAX_BIT_SIZE> g_ruleIntention;
24 static std::bitset<MAX_BIT_SIZE> g_ruleBundleName;
25 static std::bitset<MAX_BIT_SIZE> g_ruleGroupId;
26 static constexpr const char *UNIFIED_KEY_SCHEMA = "udmf://";
27 static constexpr const char SEPARATOR = '/';
28 static constexpr std::string_view SCHEME_SEPARATOR = "://";
29 #define ALPHA_AGGREGATE "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
30 #define DIGIT_AGGREGATE "0123456789"
31 #define SYMBOL_AGGREGATE ":;<=>?@[\\]_`"
32 static constexpr std::string_view INTENTION_AGGREGATE_VIEW = ALPHA_AGGREGATE "_";
33 static constexpr std::string_view BUNDLE_AGGREGATE_VIEW = ALPHA_AGGREGATE DIGIT_AGGREGATE "._+-";
34 static constexpr std::string_view GROUPID_AGGREGATE_VIEW = ALPHA_AGGREGATE DIGIT_AGGREGATE SYMBOL_AGGREGATE;
35 static constexpr uint32_t PREFIX_LEN = 24;
36 static constexpr uint32_t SUFIX_LEN = 8;
37 static constexpr uint32_t INDEX_LEN = 32;
UnifiedKey(std::string key)38 UnifiedKey::UnifiedKey(std::string key)
39 {
40 this->key = std::move(key);
41 }
42
UnifiedKey(std::string intention,std::string bundle,std::string groupId)43 UnifiedKey::UnifiedKey(std::string intention, std::string bundle, std::string groupId)
44 {
45 this->intention = std::move(intention);
46 this->bundleName = std::move(bundle);
47 this->groupId = std::move(groupId);
48 }
49
GetUnifiedKey()50 std::string UnifiedKey::GetUnifiedKey()
51 {
52 if (!this->key.empty()) {
53 return this->key;
54 }
55 if (this->intention.empty() || this->groupId.empty()) {
56 return "";
57 }
58 // Uri-compliant structure, example: udmf://drag/com.ohos.test/012345679abc
59 std::ostringstream oss;
60 oss << UNIFIED_KEY_SCHEMA << this->intention << SEPARATOR <<
61 this->bundleName << SEPARATOR << this->groupId;
62 this->key = oss.str();
63 return this->key;
64 }
65
GetKeyCommonPrefix() const66 std::string UnifiedKey::GetKeyCommonPrefix() const
67 {
68 if (this->key.size() > INDEX_LEN) {
69 return this->key.substr(0, key.size() - SUFIX_LEN);
70 }
71 if (this->intention.empty() || this->groupId.size() < INDEX_LEN) {
72 LOG_ERROR(UDMF_FRAMEWORK, "Empty property key.intention:%{public}s, groupId:%{public}s",
73 intention.c_str(), groupId.c_str());
74 return "";
75 }
76 std::ostringstream oss;
77 oss << UNIFIED_KEY_SCHEMA << this->intention << SEPARATOR <<
78 this->bundleName << SEPARATOR << this->groupId.substr(0, PREFIX_LEN);
79 return oss.str();
80 }
81
IsValid()82 bool UnifiedKey::IsValid()
83 {
84 if (key.empty()) {
85 LOG_ERROR(UDMF_FRAMEWORK, "Empty key");
86 return false;
87 }
88 PreliminaryWork();
89 std::string_view data = key;
90 size_t pos = data.find(SCHEME_SEPARATOR);
91 if (pos == std::string::npos) {
92 LOG_ERROR(UDMF_FRAMEWORK, "Missing scheme separator. Key=%{public}s", this->key.c_str());
93 return false;
94 }
95 auto schema = data.substr(0, pos + SCHEME_SEPARATOR.length());
96 if (schema != UNIFIED_KEY_SCHEMA) {
97 LOG_ERROR(UDMF_FRAMEWORK, "Invalid key schema. Key=%{public}s", this->key.c_str());
98 return false;
99 }
100 data = data.substr(pos + SCHEME_SEPARATOR.length()); // intention/bundle/group
101 if (!ExtractAndValidateSegment(data, this->intention, g_ruleIntention, "intention")) {
102 return false;
103 }
104 if (!ExtractAndValidateSegment(data, this->bundleName, g_ruleBundleName, "bundleName")) {
105 return false;
106 }
107 if (data.empty()) {
108 LOG_ERROR(UDMF_FRAMEWORK, "Find groupId error. Key=%{public}s", this->key.c_str());
109 return false;
110 }
111 if (!CheckCharacter(data, g_ruleGroupId)) {
112 LOG_ERROR(UDMF_FRAMEWORK, "Check groupId character error. Key=%{public}s", this->key.c_str());
113 return false;
114 }
115 this->groupId = data;
116 return true;
117 }
118
ExtractAndValidateSegment(std::string_view & data,std::string & field,const std::bitset<MAX_BIT_SIZE> & rule,const std::string & name)119 bool UnifiedKey::ExtractAndValidateSegment(std::string_view& data, std::string& field,
120 const std::bitset<MAX_BIT_SIZE>& rule, const std::string& name)
121 {
122 size_t pos = data.find(SEPARATOR);
123 if (pos == std::string_view::npos) {
124 LOG_ERROR(UDMF_FRAMEWORK, "Missing '/' for %{public}s", name.c_str());
125 return false;
126 }
127 field = std::string(data.substr(0, pos));
128 if (!CheckCharacter(field, rule)) {
129 LOG_ERROR(UDMF_FRAMEWORK, "Invalid character in %{public}s", name.c_str());
130 return false;
131 }
132 // Remove the prefix '/'
133 data = data.substr(pos + 1);
134 return true;
135 }
136
CheckCharacter(const std::string_view & data,const std::bitset<MAX_BIT_SIZE> & rule)137 bool UnifiedKey::CheckCharacter(const std::string_view& data, const std::bitset<MAX_BIT_SIZE> &rule)
138 {
139 if (data.empty()) {
140 LOG_DEBUG(UDMF_FRAMEWORK, "empty key");
141 return false;
142 }
143 size_t dataLen = data.size();
144 for (size_t i = 0; i < dataLen; ++i) {
145 if (static_cast<int>(data[i]) >= 0 && static_cast<int>(data[i]) < 128) { // 128:ASCII Max Number
146 bool isLegal = rule.test(data[i]);
147 if (!isLegal) {
148 return false;
149 }
150 }
151 }
152 return true;
153 }
154
PreliminaryWork()155 void UnifiedKey::PreliminaryWork()
156 {
157 // All intentions are composed of uppercase and lowercase letters and underscores.
158 if (g_ruleIntention.none()) {
159 for (char i : INTENTION_AGGREGATE_VIEW) {
160 g_ruleIntention.set(i);
161 }
162 }
163 // All bundle name are composed of uppercase and lowercase letters and dots.
164 if (g_ruleBundleName.none()) {
165 for (char i : BUNDLE_AGGREGATE_VIEW) {
166 g_ruleBundleName.set(i);
167 }
168 }
169 // Characters of groupId are taken from Ascii codes 48 to 122.
170 if (g_ruleGroupId.none()) {
171 for (char i : GROUPID_AGGREGATE_VIEW) {
172 g_ruleGroupId.set(i);
173 }
174 }
175 }
176 } // namespace UDMF
177 } // namespace OHOS