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 "UtdClient"
16#include <regex>
17#include <thread>
18#include "utd_client.h"
19#include "logger.h"
20#include "utd_graph.h"
21#include "custom_utd_store.h"
22#include "accesstoken_kit.h"
23#include "ipc_skeleton.h"
24#include "os_account_manager.h"
25namespace OHOS {
26namespace UDMF {
27constexpr const int MAX_UTD_LENGTH = 256;
28
29UtdClient::UtdClient()
30{
31    if (!Init()) {
32        LOG_WARN(UDMF_CLIENT, "construct UtdClient failed, try again.");
33        auto updateTask = []() {
34            std::this_thread::sleep_for(std::chrono::seconds(3));
35            UtdClient::GetInstance().Init();
36        };
37        std::thread(updateTask).detach();
38    }
39    LOG_INFO(UDMF_CLIENT, "construct UtdClient sucess.");
40}
41
42UtdClient::~UtdClient()
43{
44}
45
46UtdClient &UtdClient::GetInstance()
47{
48    static auto instance = new UtdClient();
49    return *instance;
50}
51
52bool UtdClient::Init()
53{
54    bool result = true;
55    std::unique_lock<std::shared_mutex> lock(utdMutex_);
56    descriptorCfgs_ = PresetTypeDescriptors::GetInstance().GetPresetTypes();
57    std::vector<TypeDescriptorCfg> customTypes;
58    if (IsHapTokenType()) {
59        customTypes = CustomUtdStore::GetInstance().GetHapTypeCfgs();
60    } else {
61        int32_t userId = DEFAULT_USER_ID;
62        if (GetCurrentActiveUserId(userId) != Status::E_OK) {
63            result = false;
64        }
65        customTypes = CustomUtdStore::GetInstance().GetTypeCfgs(userId);
66    }
67    LOG_INFO(UDMF_CLIENT, "get customUtd, size:%{public}zu", customTypes.size());
68    if (!customTypes.empty()) {
69        descriptorCfgs_.insert(descriptorCfgs_.end(), customTypes.begin(), customTypes.end());
70    }
71    UtdGraph::GetInstance().InitUtdGraph(descriptorCfgs_);
72    return result;
73}
74
75Status UtdClient::GetTypeDescriptor(const std::string &typeId, std::shared_ptr<TypeDescriptor> &descriptor)
76{
77    {
78        std::shared_lock<std::shared_mutex> guard(utdMutex_);
79        for (const auto &utdTypeCfg : descriptorCfgs_) {
80            if (utdTypeCfg.typeId == typeId) {
81                descriptor = std::make_shared<TypeDescriptor>(utdTypeCfg);
82                LOG_DEBUG(UDMF_CLIENT, "get descriptor success. %{public}s ", typeId.c_str());
83                return Status::E_OK;
84            }
85        }
86    }
87    if (typeId.find(FLEXIBLE_TYPE_FLAG) != typeId.npos) {
88        LOG_DEBUG(UDMF_CLIENT, "get flexible descriptor. %{public}s ", typeId.c_str());
89        return GetFlexibleTypeDescriptor(typeId, descriptor);
90    }
91    return Status::E_OK;
92}
93
94bool UtdClient::IsValidFileExtension(const std::string &fileExtension)
95{
96    if (fileExtension.empty()) {
97        return false;
98    }
99    if (fileExtension[0] != '.' || fileExtension.find("?") != fileExtension.npos ||
100        fileExtension.find(":") != fileExtension.npos || fileExtension.find("=") != fileExtension.npos ||
101        fileExtension.find("\\") != fileExtension.npos) {
102            return false;
103    }
104
105    return true;
106}
107
108bool UtdClient::IsValidMimeType(const std::string &mimeType)
109{
110    if (mimeType.empty()) {
111        return false;
112    }
113    if (mimeType.find("?") != mimeType.npos || mimeType.find(":") != mimeType.npos ||
114        mimeType.find("=") != mimeType.npos ||mimeType.find("\\") != mimeType.npos) {
115            return false;
116    }
117    return true;
118}
119
120Status UtdClient::GetFlexibleTypeDescriptor(const std::string &typeId, std::shared_ptr<TypeDescriptor> &descriptor)
121{
122    TypeDescriptorCfg flexibleTypeDescriptorCfg;
123    if (!FlexibleType::ParseFlexibleUtd(typeId, flexibleTypeDescriptorCfg)) {
124        LOG_ERROR(UDMF_CLIENT, "ParseFlexibleUtd failed, invalid typeId: %{public}s", typeId.c_str());
125        return Status::E_ERROR;
126    }
127    descriptor = std::make_shared<TypeDescriptor>(flexibleTypeDescriptorCfg);
128    return Status::E_OK;
129}
130
131Status UtdClient::GetUniformDataTypeByFilenameExtension(const std::string &fileExtension, std::string &typeId,
132                                                        std::string belongsTo)
133{
134    std::string lowerFileExtension = fileExtension;
135    std::transform(lowerFileExtension.begin(), lowerFileExtension.end(), lowerFileExtension.begin(), ::tolower);
136    if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
137        LOG_ERROR(UDMF_CLIENT, "invalid belongsTo. fileExtension:%{public}s, belongsTo:%{public}s ",
138                  fileExtension.c_str(), belongsTo.c_str());
139        return Status::E_INVALID_PARAMETERS;
140    }
141    {
142        std::shared_lock<std::shared_mutex> guard(utdMutex_);
143        bool found = false;
144        for (const auto &utdTypeCfg : descriptorCfgs_) {
145            for (auto fileEx : utdTypeCfg.filenameExtensions) {
146                std::transform(fileEx.begin(), fileEx.end(), fileEx.begin(), ::tolower);
147                if (fileEx == lowerFileExtension) {
148                    typeId = utdTypeCfg.typeId;
149                    found = true;
150                    break;
151                }
152            }
153            if (found) {
154                break;
155            }
156        }
157    }
158    // the find typeId is not belongsTo to the belongsTo.
159    if (!typeId.empty() && belongsTo != DEFAULT_TYPE_ID && belongsTo != typeId &&
160        !UtdGraph::GetInstance().IsLowerLevelType(belongsTo, typeId)) {
161        typeId = "";
162    }
163
164    if (typeId.empty()) {
165        if (!IsValidFileExtension(lowerFileExtension)) {
166            LOG_ERROR(UDMF_CLIENT, "invalid fileExtension. fileExtension:%{public}s, belongsTo:%{public}s ",
167                      fileExtension.c_str(), belongsTo.c_str());
168            return Status::E_INVALID_PARAMETERS;
169        }
170        typeId = FlexibleType::GenFlexibleUtd("", lowerFileExtension, belongsTo);
171    }
172    return Status::E_OK;
173}
174
175Status UtdClient::GetUniformDataTypesByFilenameExtension(const std::string &fileExtension,
176    std::vector<std::string> &typeIds, const std::string &belongsTo)
177{
178    if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
179        LOG_ERROR(UDMF_CLIENT, "invalid belongsTo. fileExtension:%{public}s, belongsTo:%{public}s ",
180            fileExtension.c_str(), belongsTo.c_str());
181        return Status::E_INVALID_PARAMETERS;
182    }
183    if (!IsValidFileExtension(fileExtension)) {
184        LOG_ERROR(UDMF_CLIENT, "invalid fileExtension. fileExtension:%{public}s, belongsTo:%{public}s ",
185            fileExtension.c_str(), belongsTo.c_str());
186        return Status::E_INVALID_PARAMETERS;
187    }
188
189    std::string lowerFileExtension = fileExtension;
190    std::transform(lowerFileExtension.begin(), lowerFileExtension.end(), lowerFileExtension.begin(), ::tolower);
191    std::vector<std::string> typeIdsInCfg;
192    {
193        std::shared_lock<std::shared_mutex> guard(utdMutex_);
194        for (const auto &utdTypeCfg : descriptorCfgs_) {
195            for (auto fileEx : utdTypeCfg.filenameExtensions) {
196                std::transform(fileEx.begin(), fileEx.end(), fileEx.begin(), ::tolower);
197                if (fileEx == lowerFileExtension) {
198                    typeIdsInCfg.push_back(utdTypeCfg.typeId);
199                    break;
200                }
201            }
202        }
203    }
204    typeIds.clear();
205    for (const auto &typeId : typeIdsInCfg) {
206        // the find typeId is not belongsTo to the belongsTo.
207        if (belongsTo != DEFAULT_TYPE_ID && belongsTo != typeId &&
208            !UtdGraph::GetInstance().IsLowerLevelType(belongsTo, typeId)) {
209            continue;
210        }
211        typeIds.emplace_back(typeId);
212    }
213    if (typeIds.empty()) {
214        typeIds.emplace_back(FlexibleType::GenFlexibleUtd("", lowerFileExtension, belongsTo));
215    }
216    return Status::E_OK;
217}
218
219Status UtdClient::GetUniformDataTypeByMIMEType(const std::string &mimeType, std::string &typeId,
220                                               std::string belongsTo)
221{
222    std::string lowerMimeType = mimeType;
223    std::transform(lowerMimeType.begin(), lowerMimeType.end(), lowerMimeType.begin(), ::tolower);
224    if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
225        LOG_ERROR(UDMF_CLIENT, "invalid belongsTo. mimeType:%{public}s, belongsTo:%{public}s ",
226                  mimeType.c_str(), belongsTo.c_str());
227        return Status::E_INVALID_PARAMETERS;
228    }
229    typeId = GetTypeIdFromCfg(lowerMimeType);
230    // the find typeId is not belongsTo to the belongsTo.
231    if (!typeId.empty() && belongsTo != DEFAULT_TYPE_ID && belongsTo != typeId &&
232        !UtdGraph::GetInstance().IsLowerLevelType(belongsTo, typeId)) {
233        typeId = "";
234    }
235    if (typeId.empty()) {
236        if (!IsValidMimeType(mimeType)) {
237            LOG_ERROR(UDMF_CLIENT, "invalid mimeType. mimeType:%{public}s, belongsTo:%{public}s ",
238                      mimeType.c_str(), belongsTo.c_str());
239            return Status::E_INVALID_PARAMETERS;
240        }
241        typeId = FlexibleType::GenFlexibleUtd(lowerMimeType, "", belongsTo);
242    }
243    return Status::E_OK;
244}
245
246std::string UtdClient::GetTypeIdFromCfg(const std::string &mimeType)
247{
248    std::shared_lock<std::shared_mutex> guard(utdMutex_);
249    for (const auto &utdTypeCfg : descriptorCfgs_) {
250        for (auto mime : utdTypeCfg.mimeTypes) {
251            std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
252            if (mime == mimeType) {
253                return utdTypeCfg.typeId;
254            }
255        }
256    }
257    if (mimeType.empty() || mimeType.back() != '*') {
258        return "";
259    }
260    std::string prefixType = mimeType.substr(0, mimeType.length() - 1);
261    for (const auto &utdTypeCfg : descriptorCfgs_) {
262        for (auto mime : utdTypeCfg.mimeTypes) {
263            std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
264            if (mime.rfind(prefixType, 0) == 0 && utdTypeCfg.belongingToTypes.size() > 0) {
265                return utdTypeCfg.belongingToTypes[0];
266            }
267        }
268    }
269    return "";
270}
271
272Status UtdClient::GetUniformDataTypesByMIMEType(const std::string &mimeType, std::vector<std::string> &typeIds,
273    const std::string &belongsTo)
274{
275    if (belongsTo != DEFAULT_TYPE_ID && !UtdGraph::GetInstance().IsValidType(belongsTo)) {
276        LOG_ERROR(UDMF_CLIENT, "invalid belongsTo. mimeType:%{public}s, belongsTo:%{public}s ",
277            mimeType.c_str(), belongsTo.c_str());
278        return Status::E_INVALID_PARAMETERS;
279    }
280    if (!IsValidMimeType(mimeType)) {
281        LOG_ERROR(UDMF_CLIENT, "invalid mimeType. mimeType:%{public}s, belongsTo:%{public}s ",
282            mimeType.c_str(), belongsTo.c_str());
283        return Status::E_INVALID_PARAMETERS;
284    }
285
286    std::string lowerMimeType = mimeType;
287    std::transform(lowerMimeType.begin(), lowerMimeType.end(), lowerMimeType.begin(), ::tolower);
288    std::vector<std::string> typeIdsInCfg = GetTypeIdsFromCfg(lowerMimeType);
289    typeIds.clear();
290    for (const auto &typeId : typeIdsInCfg) {
291        // the find typeId is not belongsTo to the belongsTo.
292        if (belongsTo != DEFAULT_TYPE_ID && belongsTo != typeId &&
293            !UtdGraph::GetInstance().IsLowerLevelType(belongsTo, typeId)) {
294            continue;
295        }
296        typeIds.emplace_back(typeId);
297    }
298    if (typeIds.empty()) {
299        typeIds.emplace_back(FlexibleType::GenFlexibleUtd(lowerMimeType, "", belongsTo));
300    }
301    return Status::E_OK;
302}
303
304std::vector<std::string> UtdClient::GetTypeIdsFromCfg(const std::string &mimeType)
305{
306    bool prefixMatch = false;
307    std::string prefixType;
308    if (!mimeType.empty() && mimeType.back() == '*') {
309        prefixType = mimeType.substr(0, mimeType.length() - 1);
310        prefixMatch = true;
311    }
312    std::vector<std::string> typeIdsInCfg;
313
314    std::shared_lock<std::shared_mutex> guard(utdMutex_);
315    for (const auto &utdTypeCfg : descriptorCfgs_) {
316        for (auto mime : utdTypeCfg.mimeTypes) {
317            std::transform(mime.begin(), mime.end(), mime.begin(), ::tolower);
318            if ((mime == mimeType) || (prefixMatch && mime.rfind(prefixType, 0) == 0)) {
319                typeIdsInCfg.push_back(utdTypeCfg.typeId);
320                break;
321            }
322        }
323    }
324    return typeIdsInCfg;
325}
326
327Status UtdClient::IsUtd(std::string typeId, bool &result)
328{
329    try {
330        if (typeId.empty() || typeId.size() > MAX_UTD_LENGTH) {
331            result = false;
332            return Status::E_INVALID_PARAMETERS;
333        }
334        if (typeId[0] == '.' || find(typeId.begin(), typeId.end(), '/') != typeId.end()) {
335            result = false;
336            return Status::E_OK;
337        }
338        constexpr const char *preSetTypeIdRegexRule =
339            R"(^(general\.|openharmony\.|org\.|com\.|macos\.|debian\.|redhat\.|io\.|de\.|net\.)[a-z0-9-\.]+(\-[a-z0-9-]+)*$)";
340        if (std::regex_match(typeId, std::regex(preSetTypeIdRegexRule))) {
341            result = true;
342            return Status::E_OK;
343        }
344        constexpr const char *customUtdRegexRule = R"(^([A-Za-z]\w*)(\.\w+)+(\.[A-Za-z\d-]+)+)";
345        if (std::regex_match(typeId, std::regex(customUtdRegexRule))) {
346            result = true;
347            return Status::E_OK;
348        }
349        result = false;
350    } catch (...) {
351        LOG_ERROR(UDMF_CLIENT, "exception, typeId:%{public}s", typeId.c_str());
352        result = false;
353        return Status::E_ERROR;
354    }
355    LOG_ERROR(UDMF_CLIENT, "is not utd, typeId:%{public}s", typeId.c_str());
356    return Status::E_OK;
357}
358
359bool UtdClient::IsHapTokenType()
360{
361    uint32_t tokenId = IPCSkeleton::GetSelfTokenID();
362    auto tokenType = Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(tokenId);
363    LOG_DEBUG(UDMF_CLIENT, "GetTokenTypeFlag, tokenType = %{public}d.", tokenType);
364    if (tokenType == Security::AccessToken::TypeATokenTypeEnum::TOKEN_HAP) {
365        return true;
366    }
367    return false;
368}
369
370Status UtdClient::GetCurrentActiveUserId(int32_t& userId)
371{
372    std::vector<int32_t> localIds;
373    int32_t status = OHOS::AccountSA::OsAccountManager::QueryActiveOsAccountIds(localIds);
374    if (status != Status::E_OK || localIds.empty()) {
375        LOG_ERROR(UDMF_CLIENT, "Get OsAccountId fail, status:%{public}d", status);
376        return Status::E_ERROR;
377    }
378    userId = localIds[0];
379    return Status::E_OK;
380}
381
382void UtdClient::InstallCustomUtds(const std::string &bundleName, const std::string &jsonStr, int32_t user)
383{
384    if (IsHapTokenType()) {
385        return;
386    }
387    LOG_INFO(UDMF_CLIENT, "start, bundleName:%{public}s, user:%{public}d", bundleName.c_str(), user);
388    std::vector<TypeDescriptorCfg> customTyepCfgs = CustomUtdStore::GetInstance().GetTypeCfgs(user);
389    if (!CustomUtdStore::GetInstance().UninstallCustomUtds(bundleName, user, customTyepCfgs)) {
390        LOG_ERROR(UDMF_CLIENT, "custom utd installed failed. bundleName:%{public}s, user:%{public}d",
391            bundleName.c_str(), user);
392        return;
393    }
394    UpdateGraph(customTyepCfgs);
395    if (!jsonStr.empty()) {
396        if (!CustomUtdStore::GetInstance().InstallCustomUtds(bundleName, jsonStr, user, customTyepCfgs)) {
397            LOG_ERROR(UDMF_CLIENT, "no custom utd installed. bundleName:%{public}s, user:%{public}d",
398                bundleName.c_str(), user);
399            return;
400        }
401        UpdateGraph(customTyepCfgs);
402    }
403}
404
405void UtdClient::UninstallCustomUtds(const std::string &bundleName, int32_t user)
406{
407    if (IsHapTokenType()) {
408        return;
409    }
410    LOG_INFO(UDMF_CLIENT, "start, bundleName:%{public}s, user:%{public}d", bundleName.c_str(), user);
411    std::vector<TypeDescriptorCfg> customTyepCfgs = CustomUtdStore::GetInstance().GetTypeCfgs(user);
412    if (!CustomUtdStore::GetInstance().UninstallCustomUtds(bundleName, user, customTyepCfgs)) {
413        LOG_ERROR(UDMF_CLIENT, "custom utd installed failed. bundleName:%{public}s, user:%{public}d",
414            bundleName.c_str(), user);
415        return;
416    }
417    UpdateGraph(customTyepCfgs);
418}
419
420void UtdClient::UpdateGraph(const std::vector<TypeDescriptorCfg> &customTyepCfgs)
421{
422    std::vector<TypeDescriptorCfg> allTypeCfgs = PresetTypeDescriptors::GetInstance().GetPresetTypes();
423    allTypeCfgs.insert(allTypeCfgs.end(), customTyepCfgs.begin(), customTyepCfgs.end());
424    LOG_INFO(UDMF_CLIENT, "customTyepSize:%{public}zu, allTypeSize:%{public}zu",
425        customTyepCfgs.size(), allTypeCfgs.size());
426    auto graph = UtdGraph::GetInstance().ConstructNewGraph(allTypeCfgs);
427    std::unique_lock<std::shared_mutex> lock(utdMutex_);
428    UtdGraph::GetInstance().Update(std::move(graph));
429    descriptorCfgs_ = allTypeCfgs;
430}
431} // namespace UDMF
432} // namespace OHOS
433