• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "file_path_utils.h"
17 
18 #include <fstream>
19 #include <regex>
20 #include <vector>
21 
22 #include "constants.h"
23 
24 #ifdef WINDOWS_PLATFORM
25 #include <io.h>
26 
27 namespace {
realpath(const char * path,char * resolvedPath)28 char* realpath(const char* path, char* resolvedPath)
29 {
30     if (_access(path, 0) < 0) {
31         return nullptr;
32     }
33     if (strcpy_s(resolvedPath, PATH_MAX, path) != 0) {
34         return nullptr;
35     }
36     return resolvedPath;
37 }
38 }
39 #endif
40 
41 namespace OHOS {
42 namespace AbilityBase {
43 namespace {
44 constexpr char EXT_NAME_ABC[] = ".abc";
45 constexpr char EXT_NAME_ETS[] = ".ets";
46 constexpr char EXT_NAME_TS[] = ".ts";
47 constexpr char EXT_NAME_JS[] = ".js";
48 constexpr char PREFIX_BUNDLE[] = "@bundle:";
49 constexpr char PREFIX_MODULE[] = "@module:";
50 constexpr char PREFIX_LOCAL[] = "@local:";
51 constexpr char NPM_PATH_SEGMENT[] = "node_modules";
52 constexpr char NPM_ENTRY_FILE[] = "index.abc";
53 constexpr char NPM_ENTRY_LINK[] = "entry.txt";
54 constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/";
55 constexpr char OTHER_BUNDLE_INSTALL_PATH[] = "/data/bundles/";
56 
57 constexpr size_t MAX_NPM_LEVEL = 1;
58 constexpr size_t SEGMENTS_LIMIT_TWO = 2;
59 constexpr size_t SEGMENTS_LIMIT_THREE = 3;
60 } // namespace
61 
StringStartWith(const std::string & str,const char * startStr,size_t startStrLen)62 bool StringStartWith(const std::string& str, const char* startStr, size_t startStrLen)
63 {
64     return ((str.length() >= startStrLen) && (str.compare(0, startStrLen, startStr) == 0));
65 }
66 
StringEndWith(const std::string & str,const char * endStr,size_t endStrLen)67 bool StringEndWith(const std::string& str, const char* endStr, size_t endStrLen)
68 {
69     size_t len = str.length();
70     return ((len >= endStrLen) && (str.compare(len - endStrLen, endStrLen, endStr) == 0));
71 }
72 
SplitString(const std::string & str,std::vector<std::string> & out,size_t pos,const char * seps)73 void SplitString(const std::string& str, std::vector<std::string>& out, size_t pos, const char* seps)
74 {
75     if (str.empty() || pos >= str.length()) {
76         return;
77     }
78 
79     size_t startPos = pos;
80     size_t endPos = 0;
81     while ((endPos = str.find_first_of(seps, startPos)) != std::string::npos) {
82         if (endPos > startPos) {
83             out.emplace_back(str.substr(startPos, endPos - startPos));
84         }
85         startPos = endPos + 1;
86     }
87 
88     if (startPos < str.length()) {
89         out.emplace_back(str.substr(startPos));
90     }
91 }
92 
JoinString(const std::vector<std::string> & strs,char sep,size_t startIndex)93 std::string JoinString(const std::vector<std::string>& strs, char sep, size_t startIndex)
94 {
95     std::string out;
96     for (size_t index = startIndex; index < strs.size(); ++index) {
97         if (!strs[index].empty()) {
98             out.append(strs[index]) += sep;
99         }
100     }
101     if (!out.empty()) {
102         out.pop_back();
103     }
104     return out;
105 }
106 
StripString(const std::string & str,const char * charSet)107 std::string StripString(const std::string& str, const char* charSet)
108 {
109     size_t startPos = str.find_first_not_of(charSet);
110     if (startPos == std::string::npos) {
111         return std::string();
112     }
113 
114     return str.substr(startPos, str.find_last_not_of(charSet) - startPos + 1);
115 }
116 
FixExtName(std::string & path)117 void FixExtName(std::string& path)
118 {
119     if (path.empty()) {
120         return;
121     }
122 
123     if (StringEndWith(path, EXT_NAME_ABC, sizeof(EXT_NAME_ABC) - 1)) {
124         return;
125     }
126 
127     if (StringEndWith(path, EXT_NAME_ETS, sizeof(EXT_NAME_ETS) - 1)) {
128         path.erase(path.length() - (sizeof(EXT_NAME_ETS) - 1), sizeof(EXT_NAME_ETS) - 1);
129     } else if (StringEndWith(path, EXT_NAME_TS, sizeof(EXT_NAME_TS) - 1)) {
130         path.erase(path.length() - (sizeof(EXT_NAME_TS) - 1), sizeof(EXT_NAME_TS) - 1);
131     } else if (StringEndWith(path, EXT_NAME_JS, sizeof(EXT_NAME_JS) - 1)) {
132         path.erase(path.length() - (sizeof(EXT_NAME_JS) - 1), sizeof(EXT_NAME_JS) - 1);
133     }
134 
135     path.append(EXT_NAME_ABC);
136 }
137 
GetInstallPath(const std::string & curJsModulePath,bool module)138 std::string GetInstallPath(const std::string& curJsModulePath, bool module)
139 {
140     size_t pos = std::string::npos;
141     if (StringStartWith(curJsModulePath, BUNDLE_INSTALL_PATH, std::string(BUNDLE_INSTALL_PATH).length())) {
142         pos = std::string(BUNDLE_INSTALL_PATH).length() - 1;
143     } else {
144         if (!StringStartWith(curJsModulePath, OTHER_BUNDLE_INSTALL_PATH,
145             std::string(OTHER_BUNDLE_INSTALL_PATH).length())) {
146             return std::string();
147         }
148 
149         pos = curJsModulePath.find('/', std::string(OTHER_BUNDLE_INSTALL_PATH).length());
150         if (pos == std::string::npos) {
151             return std::string();
152         }
153     }
154 
155     if (module) {
156         pos = curJsModulePath.find('/', pos + 1);
157         if (pos == std::string::npos) {
158             return std::string();
159         }
160     }
161 
162     return curJsModulePath.substr(0, pos + 1);
163 }
164 
MakeNewJsModulePath(const std::string & curJsModulePath,const std::string & newJsModuleUri)165 std::string MakeNewJsModulePath(
166     const std::string& curJsModulePath, const std::string& newJsModuleUri)
167 {
168     std::string moduleInstallPath = GetInstallPath(curJsModulePath, true);
169     if (moduleInstallPath.empty()) {
170         return std::string();
171     }
172 
173     std::vector<std::string> pathVector;
174     SplitString(curJsModulePath, pathVector, moduleInstallPath.length());
175 
176     if (pathVector.empty()) {
177         return std::string();
178     }
179 
180     // Remove file name, reserve only dir name
181     pathVector.pop_back();
182 
183     std::vector<std::string> relativePathVector;
184     SplitString(newJsModuleUri, relativePathVector);
185 
186     for (auto& value : relativePathVector) {
187         if (value == ".") {
188             continue;
189         } else if (value == "..") {
190             if (pathVector.empty()) {
191                 return std::string();
192             }
193             pathVector.pop_back();
194         } else {
195             pathVector.emplace_back(std::move(value));
196         }
197     }
198 
199     std::string jsModulePath = moduleInstallPath + JoinString(pathVector, '/');
200     FixExtName(jsModulePath);
201     if (jsModulePath.size() >= PATH_MAX) {
202         return std::string();
203     }
204 
205     char path[PATH_MAX];
206     if (realpath(jsModulePath.c_str(), path) != nullptr) {
207         return std::string(path);
208     }
209     return std::string();
210 }
211 
FindNpmPackageInPath(const std::string & npmPath)212 std::string FindNpmPackageInPath(const std::string& npmPath)
213 {
214     std::string fileName = npmPath + "/" + NPM_ENTRY_FILE;
215 
216     char path[PATH_MAX];
217     if (fileName.size() >= PATH_MAX) {
218         return std::string();
219     }
220     if (realpath(fileName.c_str(), path) != nullptr) {
221         return path;
222     }
223 
224     fileName = npmPath + "/" + NPM_ENTRY_LINK;
225     if (fileName.size() >= PATH_MAX) {
226         return std::string();
227     }
228     if (realpath(fileName.c_str(), path) == nullptr) {
229         return std::string();
230     }
231 
232     std::ifstream stream(path, std::ios::ate);
233     if (!stream.is_open()) {
234         return std::string();
235     }
236 
237     auto fileLength = stream.tellg();
238     if (fileLength >= PATH_MAX) {
239         stream.close();
240         return std::string();
241     }
242 
243     stream.seekg(0);
244     stream.read(path, fileLength);
245     path[fileLength] = '\0';
246     stream.close();
247 
248     std::string npmPackagePathString = npmPath + '/' + StripString(path);
249     if (npmPackagePathString.size() >= PATH_MAX) {
250         return std::string();
251     }
252     if (realpath(npmPackagePathString.c_str(), path) == nullptr) {
253         return std::string();
254     }
255     return path;
256 }
257 
FindNpmPackageInTopLevel(const std::string & moduleInstallPath,const std::string & npmPackage,size_t start)258 std::string FindNpmPackageInTopLevel(
259     const std::string& moduleInstallPath, const std::string& npmPackage, size_t start)
260 {
261     for (size_t startLevel = start; startLevel <= MAX_NPM_LEVEL; ++startLevel) {
262         std::string pathString = moduleInstallPath + NPM_PATH_SEGMENT + '/' +
263             std::to_string(startLevel) + '/' + npmPackage;
264         pathString = FindNpmPackageInPath(pathString);
265         if (!pathString.empty()) {
266             return pathString;
267         }
268     }
269 
270     return std::string();
271 }
272 
FindNpmPackage(const std::string & curJsModulePath,const std::string & npmPackageString)273 std::string FindNpmPackage(const std::string& curJsModulePath, const std::string& npmPackageString)
274 {
275     std::string newJsModulePathString = MakeNewJsModulePath(curJsModulePath, npmPackageString);
276     if (!newJsModulePathString.empty()) {
277         return newJsModulePathString;
278     }
279     std::string moduleInstallPath = GetInstallPath(curJsModulePath);
280     if (moduleInstallPath.empty()) {
281         return std::string();
282     }
283     std::vector<std::string> pathVector;
284     SplitString(curJsModulePath, pathVector, moduleInstallPath.length());
285     if (pathVector.empty()) {
286         return std::string();
287     }
288 
289     if (pathVector[0] != NPM_PATH_SEGMENT) {
290         return FindNpmPackageInTopLevel(moduleInstallPath, npmPackageString);
291     }
292 
293     // Remove file name, reserve only dir name
294     pathVector.pop_back();
295 
296     // Find npm package until reach top level npm path such as 'node_modules/0',
297     // so there must be 2 element in vector
298     while (pathVector.size() > 2) {
299         std::string path =
300             moduleInstallPath + JoinString(pathVector, '/') + '/' + NPM_PATH_SEGMENT + '/' + npmPackageString;
301         path = FindNpmPackageInPath(path);
302         if (!path.empty()) {
303             return path;
304         }
305 
306         pathVector.pop_back();
307     }
308 
309     char* p = nullptr;
310     size_t indexNum = std::strtoul(pathVector.back().c_str(), &p, 10);
311     if (p == nullptr || *p != '\0') {
312         return std::string();
313     }
314 
315     return FindNpmPackageInTopLevel(moduleInstallPath, npmPackageString, indexNum);
316 }
317 
ParseOhmUri(const std::string & originBundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)318 std::string ParseOhmUri(
319     const std::string& originBundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri)
320 {
321     std::string moduleInstallPath;
322     std::vector<std::string> pathVector;
323     size_t indexNumber = 0;
324 
325     if (StringStartWith(newJsModuleUri, PREFIX_BUNDLE, sizeof(PREFIX_BUNDLE) - 1)) {
326         SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_BUNDLE) - 1);
327 
328         // Uri should have atleast 3 segments
329         if (pathVector.size() < SEGMENTS_LIMIT_THREE) {
330             return std::string();
331         }
332 
333         const auto& bundleName = pathVector[indexNumber++];
334         if (bundleName == originBundleName) {
335             moduleInstallPath = std::string(BUNDLE_INSTALL_PATH);
336         } else {
337             moduleInstallPath = std::string(OTHER_BUNDLE_INSTALL_PATH);
338             moduleInstallPath.append(bundleName).append("/");
339         }
340         moduleInstallPath.append(pathVector[indexNumber++]).append("/");
341     } else if (StringStartWith(newJsModuleUri, PREFIX_MODULE, sizeof(PREFIX_MODULE) - 1)) {
342         SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_MODULE) - 1);
343 
344         // Uri should have atleast 2 segments
345         if (pathVector.size() < SEGMENTS_LIMIT_TWO) {
346             return std::string();
347         }
348 
349         moduleInstallPath = GetInstallPath(curJsModulePath, false);
350         if (moduleInstallPath.empty()) {
351             return std::string();
352         }
353         moduleInstallPath.append(pathVector[indexNumber++]).append("/");
354     } else if (StringStartWith(newJsModuleUri, PREFIX_LOCAL, sizeof(PREFIX_LOCAL) - 1)) {
355         SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_LOCAL) - 1);
356 
357         if (pathVector.empty()) {
358             return std::string();
359         }
360 
361         moduleInstallPath = GetInstallPath(curJsModulePath);
362         if (moduleInstallPath.empty()) {
363             return std::string();
364         }
365     } else {
366         return std::string();
367     }
368 
369     if (pathVector[indexNumber] != NPM_PATH_SEGMENT) {
370         return moduleInstallPath + JoinString(pathVector, '/', indexNumber);
371     }
372 
373     return FindNpmPackageInTopLevel(moduleInstallPath, JoinString(pathVector, '/', indexNumber + 1));
374 }
375 
NormalizeUri(const std::string & bundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)376 std::string NormalizeUri(
377     const std::string& bundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri)
378 {
379     std::string newJsModulePath;
380     if (curJsModulePath.empty() || newJsModuleUri.empty()) {
381         return newJsModulePath;
382     }
383 
384     std::string normalizeUri = newJsModuleUri;
385     std::replace(normalizeUri.begin(), normalizeUri.end(), '\\', '/');
386 
387     switch (normalizeUri[0]) {
388         case '.': {
389             newJsModulePath = MakeNewJsModulePath(curJsModulePath, normalizeUri);
390             break;
391         }
392         case '@': {
393             newJsModulePath = ParseOhmUri(bundleName, curJsModulePath, normalizeUri);
394             if (newJsModulePath.empty()) {
395                 newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri);
396             }
397             break;
398         }
399         default: {
400             newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri);
401             break;
402         }
403     }
404 
405     FixExtName(newJsModulePath);
406     return newJsModulePath;
407 }
408 
MakeFilePath(const std::string & codePath,const std::string & modulePath,std::string & fileName)409 bool MakeFilePath(const std::string& codePath, const std::string& modulePath, std::string& fileName)
410 {
411     std::string path(codePath);
412     path.append("/").append(modulePath);
413     if (path.length() > PATH_MAX) {
414         return false;
415     }
416     char resolvedPath[PATH_MAX + 1] = { 0 };
417     if (realpath(path.c_str(), resolvedPath) != nullptr) {
418         fileName = resolvedPath;
419         return true;
420     }
421 
422     auto start = path.find_last_of('/');
423     auto end = path.find_last_of('.');
424     if (end == std::string::npos || end == 0) {
425         return false;
426     }
427 
428     auto pos = path.find_last_of('.', end - 1);
429     if (pos == std::string::npos) {
430         return false;
431     }
432 
433     path.erase(start + 1, pos - start);
434 
435     if (realpath(path.c_str(), resolvedPath) == nullptr) {
436         return false;
437     }
438 
439     fileName = resolvedPath;
440     return true;
441 }
442 
GetLoadPath(const std::string & hapPath)443 std::string GetLoadPath(const std::string& hapPath)
444 {
445     std::regex hapPattern(std::string(Constants::ABS_CODE_PATH) + std::string(Constants::FILE_SEPARATOR));
446     std::string loadPath = std::regex_replace(hapPath, hapPattern, "");
447     loadPath = std::string(Constants::LOCAL_CODE_PATH) + std::string(Constants::FILE_SEPARATOR) +
448         loadPath.substr(loadPath.find(std::string(Constants::FILE_SEPARATOR)) + 1);
449     return loadPath;
450 }
451 
GetRelativePath(const std::string & srcPath)452 std::string GetRelativePath(const std::string& srcPath)
453 {
454     if (srcPath.empty() || srcPath[0] != '/') {
455         return srcPath;
456     }
457     std::regex srcPattern(Constants::LOCAL_CODE_PATH);
458     std::string relativePath = std::regex_replace(srcPath, srcPattern, "");
459     if (relativePath.find(Constants::FILE_SEPARATOR) == 0) {
460         relativePath = relativePath.substr(1);
461         relativePath = relativePath.substr(relativePath.find(std::string(Constants::FILE_SEPARATOR)) + 1);
462     }
463     return relativePath;
464 }
465 }  // namespace AbilityBase
466 }  // namespace OHOS
467