1 /*
2 * Copyright (c) 2020-2021 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 "global_utils.h"
17
18 #include <ctype.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <securec.h>
22 #if (defined(_WIN32) || defined(_WIN64))
23 #include <shlwapi.h>
24 #endif
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #define MAX_ID_ITEM_NUM 0x7F
31 #define MAX_RES_CONFIG_NUM 0xFFFF
32 #define MAX_ITEM_LENGTH 0xFF
33
34 enum LocaleIndex {
35 LANGUAGE_INDEX = 0,
36 SCRIPT_INDEX = 1,
37 REGION_INDEX = 2
38 };
39
40 static int32_t Split(const char *src, const char *separator, char **dest, int32_t *num);
41 static int32_t SplitLocale(const char *src, char **dest, int32_t *num);
42 static uint32_t ConvertUint8ArrayToUint32(const uint8_t *array, int32_t count);
43 static uint32_t GetValueFromLocale(const char *locale);
44 static void SetLocaleItemViaKeys(const KeyParam *keyParam, LocaleItem *localeItem);
45 static uint32_t FindOffsetByLanguage(const char *language, const Key *keys, uint32_t configNum);
46 static uint32_t FindOffsetByLangWithScriptOrRegion(char **resConfig, const Key *keys, uint32_t configNum);
47 static uint32_t FindOffsetByAllParam(char **resConfig, const Key *keys, uint32_t configNum);
48 static uint32_t GetIdHeaderOffsetByCount(char **resConfig, const Key *keys, uint32_t configNum, int32_t count);
49 static uint32_t GetOffsetByLocale(const char *path, const char *locale, uint32_t length);
50 static uint32_t GetDefaultOffsetValue(int32_t file);
51 static uint32_t GetKeyValue(int32_t file);
52 static int32_t GetIdItem(int32_t file, uint32_t offset, IdItem *idItem);
53 static void FreeKeyParams(Key *keys, int32_t count);
54 static int32_t GetKeyParams(int32_t file, Key *keys, uint32_t resConfigNum);
55 static uint32_t GetIdHeaderOffsetByLocale(const char *locale, const Key *keys, uint32_t configNum);
56 static int32_t GetIdHeaderByOffset(int32_t file, uint32_t offset, IdHeader *idHeader);
57 static int32_t CheckFilePath(const char *path, char *realResourcePath, int32_t length);
58
59 const static GlobalUtilsImpl g_globalUtilsImpl = {
60 .GetOffsetByLocale = GetOffsetByLocale,
61 .GetDefaultOffsetValue = GetDefaultOffsetValue,
62 .GetKeyValue = GetKeyValue,
63 .GetIdItem = GetIdItem,
64 .GetIdHeaderOffsetByLocale = GetIdHeaderOffsetByLocale,
65 .GetIdHeaderByOffset = GetIdHeaderByOffset,
66 .SplitLocale = SplitLocale,
67 .CheckFilePath = CheckFilePath,
68 };
69
70 static uint32_t g_defaultIdHeaderOffset = INVALID_OFFSET;
71
Split(const char * src,const char * separator,char ** dest,int32_t * num)72 static int32_t Split(const char *src, const char *separator, char **dest, int32_t *num)
73 {
74 char *next = NULL;
75 int32_t count = 0;
76 if (src == NULL || src[0] == '\0' || separator == NULL || separator[0] == '\0' || dest == NULL || num == NULL) {
77 return MC_FAILURE;
78 }
79 char *temp = NULL;
80 next = strtok_r((char *)src, separator, &temp);
81 while (next != NULL) {
82 if (count >= LOCALE_ELEMENT_NUM) {
83 return MC_FAILURE;
84 }
85 *dest++ = next;
86 ++count;
87 next = strtok_r(NULL, separator, &temp);
88 }
89 *num = count;
90 return MC_SUCCESS;
91 }
92
SplitLocale(const char * src,char ** dest,int32_t * num)93 static int32_t SplitLocale(const char *src, char **dest, int32_t *num)
94 {
95 if (src == NULL || strlen(src) == 0 || dest == NULL || num == NULL) {
96 return MC_FAILURE;
97 }
98 if (strchr(src, '-') != NULL) {
99 (void)Split(src, "-", dest, num);
100 } else if (strchr(src, '_') != NULL) {
101 (void)Split(src, "_", dest, num);
102 } else {
103 dest[0] = (char *)src;
104 }
105 return MC_SUCCESS;
106 }
107
ConvertUint8ArrayToUint32(const uint8_t * array,int32_t count)108 static uint32_t ConvertUint8ArrayToUint32(const uint8_t *array, int32_t count)
109 {
110 if (array == NULL) {
111 return 0;
112 }
113 uint32_t value = 0;
114 uint8_t offset = 0;
115 // calculate big endian array, e.g. "ne\0\0" = 'n' + 'e' << 8 + 0 << 16 + 0 << 24
116 for (int32_t i = 0; i < count; i++) {
117 value += (array[i] << offset);
118 offset += OFFSET_VALUE_STEP;
119 }
120 return value;
121 }
122
GetValueFromLocale(const char * locale)123 static uint32_t GetValueFromLocale(const char *locale)
124 {
125 if (locale == NULL) {
126 return 0;
127 }
128 uint32_t value = 0;
129 uint32_t offset = 0;
130 // calculate little endian array, e.g. "en" = 'n' + 'e' << 8
131 for (int32_t i = strlen(locale) - 1; i >= 0; i--) {
132 // locale needs to be case insensitive
133 value += (((uint8_t)tolower(locale[i])) << offset);
134 offset += OFFSET_VALUE_STEP;
135 }
136 return value;
137 }
138
FindOffsetByLanguage(const char * language,const Key * keys,uint32_t configNum)139 static uint32_t FindOffsetByLanguage(const char *language, const Key *keys, uint32_t configNum)
140 {
141 if (language == NULL || keys == NULL) {
142 return INVALID_OFFSET;
143 }
144 uint32_t value = GetValueFromLocale(language);
145 for (uint32_t i = 0; i < configNum; i++) {
146 for (uint32_t j = 0; j < keys[i].keysCount; j++) {
147 if (keys[i].keyParams[j].type == LANGUAGES && keys[i].keyParams[j].value == value) {
148 return keys[i].offset;
149 }
150 }
151 }
152 return INVALID_OFFSET;
153 }
154
SetLocaleItemViaKeys(const KeyParam * keyParam,LocaleItem * localeItem)155 static void SetLocaleItemViaKeys(const KeyParam *keyParam, LocaleItem *localeItem)
156 {
157 if (keyParam == NULL || localeItem == NULL) {
158 return;
159 }
160 switch (keyParam->type) {
161 case LANGUAGES:
162 localeItem->language = keyParam->value;
163 break;
164 case SCRIPT:
165 localeItem->script = keyParam->value;
166 break;
167 case REGION:
168 localeItem->region = keyParam->value;
169 break;
170 default:
171 // unspported type
172 break;
173 }
174 }
175
FindOffsetByLangWithScriptOrRegion(char ** resConfig,const Key * keys,uint32_t configNum)176 static uint32_t FindOffsetByLangWithScriptOrRegion(char **resConfig, const Key *keys, uint32_t configNum)
177 {
178 if (resConfig == NULL || keys == NULL) {
179 return INVALID_OFFSET;
180 }
181 uint32_t offset = INVALID_OFFSET;
182 uint32_t languageValue = GetValueFromLocale(resConfig[LANGUAGE_INDEX]);
183 uint32_t secondValue = GetValueFromLocale(resConfig[1]); // secondValue may be script or region
184 uint32_t len = strlen(resConfig[1]);
185 for (uint32_t i = 0; i < configNum; i++) {
186 LocaleItem locale = {0, 0, 0};
187 for (uint32_t j = 0; j < keys[i].keysCount; j++) {
188 SetLocaleItemViaKeys(&(keys[i].keyParams[j]), &locale);
189 }
190 if (len == SCRIPT_LENGTH) { // script length is fixed at 4
191 // if all matched, just return the offset
192 if (languageValue == locale.language && secondValue == locale.script) {
193 offset = keys[i].offset;
194 break;
195 }
196 } else {
197 // if all matched, just return the offset
198 if (languageValue == locale.language && secondValue == locale.region) {
199 offset = keys[i].offset;
200 break;
201 }
202 }
203 // if only language matched, use 1st found language
204 if (offset == INVALID_OFFSET && languageValue == locale.language) {
205 offset = keys[i].offset;
206 }
207 }
208 return offset;
209 }
210
FindOffsetByAllParam(char ** resConfig,const Key * keys,uint32_t configNum)211 static uint32_t FindOffsetByAllParam(char **resConfig, const Key *keys, uint32_t configNum)
212 {
213 if (resConfig == NULL || keys == NULL) {
214 return INVALID_OFFSET;
215 }
216 uint32_t offset = INVALID_OFFSET;
217 uint32_t languageValue = GetValueFromLocale(resConfig[LANGUAGE_INDEX]);
218 uint32_t scriptValue = GetValueFromLocale(resConfig[SCRIPT_INDEX]);
219 uint32_t regionValue = GetValueFromLocale(resConfig[REGION_INDEX]);
220 uint32_t retOffsets[ALL_PARAM_LENGTH] = {0};
221 for (uint32_t i = 0; i < configNum; i++) {
222 LocaleItem locale = {0, 0, 0};
223 for (uint32_t j = 0; j < keys[i].keysCount; j++) {
224 SetLocaleItemViaKeys(&(keys[i].keyParams[j]), &locale);
225 }
226 // if all matched, save offset to retOffsets[0], the highest priority, and stop searching
227 if (retOffsets[0] == INVALID_OFFSET && languageValue == locale.language &&
228 scriptValue == locale.script && regionValue == locale.region) {
229 retOffsets[0] = keys[i].offset; // retOffsets[0] has the highest priority
230 break;
231 }
232 // if language and region matched, save 1st found offset to retOffsets[1]
233 if (retOffsets[1] == INVALID_OFFSET && languageValue == locale.language && regionValue == locale.region) {
234 retOffsets[1] = keys[i].offset; // retOffsets[1] has the second highest priority
235 continue;
236 }
237 // if language and script matched, save 1st found offset to retOffsets[2]
238 if (retOffsets[2] == INVALID_OFFSET && languageValue == locale.language && scriptValue == locale.script) {
239 retOffsets[2] = keys[i].offset; // retOffsets[2] has the third highest priority
240 continue;
241 }
242 // if only language matched, save 1st found offset to retOffsets[3], the lowest priority
243 if (retOffsets[3] == INVALID_OFFSET && languageValue == locale.language) {
244 retOffsets[3] = keys[i].offset; // retOffsets[3] has the lowest priority
245 continue;
246 }
247 }
248 // search the existed highest priority retOffsets[i]
249 for (uint32_t i = 0; i < ALL_PARAM_LENGTH; i++) {
250 if (retOffsets[i] != INVALID_OFFSET) {
251 offset = retOffsets[i];
252 break;
253 }
254 }
255 return offset;
256 }
257
GetIdHeaderOffsetByCount(char ** resConfig,const Key * keys,uint32_t configNum,int32_t count)258 static uint32_t GetIdHeaderOffsetByCount(char **resConfig, const Key *keys, uint32_t configNum, int32_t count)
259 {
260 if (resConfig == NULL || keys == NULL) {
261 return INVALID_OFFSET;
262 }
263 uint32_t offset = INVALID_OFFSET;
264 switch (count) {
265 case 1: // locale has only 1 element which is language
266 offset = FindOffsetByLanguage(resConfig[0], keys, configNum);
267 break;
268 case 2: // locale has 2 element: language & script, or language & region
269 offset = FindOffsetByLangWithScriptOrRegion(resConfig, keys, configNum);
270 break;
271 case 3: // locale has all 3 element: language script, and region
272 offset = FindOffsetByAllParam(resConfig, keys, configNum);
273 break;
274 default:
275 break;
276 }
277 return offset;
278 }
279
FreeKeyParams(Key * keys,int32_t count)280 static void FreeKeyParams(Key *keys, int32_t count)
281 {
282 if (keys == NULL) {
283 return;
284 }
285 for (int32_t i = 0; i < count; i++) {
286 if (keys[i].keysCount != 0) {
287 free(keys[i].keyParams);
288 keys[i].keyParams = NULL;
289 keys[i].keysCount = 0;
290 }
291 }
292 }
293
GetKeyParams(int32_t file,Key * keys,uint32_t resConfigNum)294 static int32_t GetKeyParams(int32_t file, Key *keys, uint32_t resConfigNum)
295 {
296 if (file < 0 || keys == NULL) {
297 return MC_FAILURE;
298 }
299 g_defaultIdHeaderOffset = INVALID_OFFSET;
300 for (uint32_t i = 0; i < resConfigNum; ++i) {
301 int seekRet = lseek(file, INDEX_DEFAULT_OFFSET, SEEK_CUR); // skip the "KEYS" header
302 if (seekRet < 0) {
303 FreeKeyParams(keys, i);
304 return MC_FAILURE;
305 }
306 keys[i].offset = GetDefaultOffsetValue(file);
307 keys[i].keysCount = GetDefaultOffsetValue(file);
308 if (keys[i].keysCount == 0) {
309 g_defaultIdHeaderOffset = keys[i].offset;
310 continue;
311 }
312 if (keys[i].keysCount > KEY_TYPE_MAX) {
313 FreeKeyParams(keys, i);
314 return MC_FAILURE;
315 }
316 keys[i].keyParams = (KeyParam *)malloc(sizeof(KeyParam) * keys[i].keysCount);
317 if (keys[i].keyParams == NULL) {
318 FreeKeyParams(keys, i);
319 return MC_FAILURE;
320 }
321 for (uint32_t j = 0; j < keys[i].keysCount; j++) {
322 keys[i].keyParams[j].type = (KeyType)GetDefaultOffsetValue(file);
323 keys[i].keyParams[j].value = GetKeyValue(file);
324 }
325 }
326 return MC_SUCCESS;
327 }
328
CheckFilePath(const char * path,char * realResourcePath,int32_t length)329 static int32_t CheckFilePath(const char *path, char *realResourcePath, int32_t length)
330 {
331 if (length > PATH_MAX || length < 0) {
332 return MC_FAILURE;
333 }
334 #if (defined(_WIN32) || defined(_WIN64))
335 if (!PathCanonicalizeA((char*)path, realResourcePath)) {
336 return MC_FAILURE;
337 }
338 return MC_SUCCESS;
339 #elif defined(I18N_PRODUCT)
340 int ret = strcpy_s(realResourcePath, PATH_MAX, path);
341 if (ret != 0) {
342 return MC_FAILURE;
343 }
344 return MC_SUCCESS;
345 #else
346 if (realpath(path, realResourcePath) == NULL) {
347 return MC_FAILURE;
348 }
349 return MC_SUCCESS;
350 #endif
351 }
352
GetOffsetByLocale(const char * path,const char * locale,uint32_t length)353 static uint32_t GetOffsetByLocale(const char *path, const char *locale, uint32_t length)
354 {
355 if (path == NULL || strlen(path) == 0 || locale == NULL || length == 0) {
356 return INVALID_OFFSET;
357 }
358 char realResourcePath[PATH_MAX] = {0};
359 if (CheckFilePath(path, realResourcePath, PATH_MAX) == MC_FAILURE) {
360 return INVALID_OFFSET;
361 }
362 int32_t file = open(realResourcePath, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH);
363 if (file < 0) {
364 return INVALID_OFFSET;
365 }
366 int seekRet = lseek(file, RES_CONFIG_NUM_OFFSET, SEEK_SET); // goto resConfigNum index, now is fixed at 132
367 if (seekRet < 0) {
368 close(file);
369 return INVALID_OFFSET;
370 }
371 uint32_t resConfigNum = GetDefaultOffsetValue(file);
372 if (resConfigNum == 0 || resConfigNum > MAX_RES_CONFIG_NUM) {
373 close(file);
374 return INVALID_OFFSET;
375 }
376 int size = sizeof(Key) * resConfigNum;
377 Key *keys = (Key *)malloc(size);
378 if (keys == NULL) {
379 close(file);
380 return INVALID_OFFSET;
381 }
382 (void)memset_s(keys, size, 0, size);
383 int32_t ret = GetKeyParams(file, keys, resConfigNum);
384 if (ret != MC_SUCCESS) {
385 close(file);
386 free(keys);
387 return INVALID_OFFSET;
388 }
389 close(file);
390 uint32_t offset = GetIdHeaderOffsetByLocale(locale, keys, resConfigNum);
391 if (offset == INVALID_OFFSET) {
392 offset = g_defaultIdHeaderOffset;
393 }
394 FreeKeyParams(keys, resConfigNum);
395 free(keys);
396 return offset;
397 }
398
GetDefaultOffsetValue(int32_t file)399 static uint32_t GetDefaultOffsetValue(int32_t file)
400 {
401 if (file < 0) {
402 return 0;
403 }
404 uint8_t cache[INDEX_DEFAULT_OFFSET] = {0};
405 int32_t ret = read(file, cache, INDEX_DEFAULT_OFFSET);
406 if (ret != INDEX_DEFAULT_OFFSET) {
407 return 0;
408 }
409 return ConvertUint8ArrayToUint32(cache, INDEX_DEFAULT_OFFSET);
410 }
411
GetKeyValue(int32_t file)412 static uint32_t GetKeyValue(int32_t file)
413 {
414 uint8_t cache[INDEX_DEFAULT_OFFSET] = {0};
415 int32_t ret = read(file, cache, INDEX_DEFAULT_OFFSET);
416 if (ret != INDEX_DEFAULT_OFFSET) {
417 return 0;
418 }
419 for (int32_t i = 0; i < INDEX_DEFAULT_OFFSET; i++) {
420 cache[i] = tolower(cache[i]); // Key value is case insensitive
421 }
422 return ConvertUint8ArrayToUint32(cache, INDEX_DEFAULT_OFFSET);
423 }
424
GetIdItem(int32_t file,uint32_t offset,IdItem * idItem)425 static int32_t GetIdItem(int32_t file, uint32_t offset, IdItem *idItem)
426 {
427 if (offset == INVALID_OFFSET || file == -1 || idItem == NULL) {
428 return MC_FAILURE;
429 }
430 uint8_t defaultCache[INDEX_DEFAULT_OFFSET] = {0};
431 uint8_t lengthCache[VALUE_LENGTH_OFFSET] = {0};
432 int seekRet = lseek(file, offset, SEEK_SET);
433 if (seekRet < 0) {
434 return MC_FAILURE;
435 }
436 (void)read(file, defaultCache, INDEX_DEFAULT_OFFSET);
437 idItem->size = ConvertUint8ArrayToUint32(defaultCache, INDEX_DEFAULT_OFFSET);
438
439 (void)read(file, defaultCache, INDEX_DEFAULT_OFFSET);
440 idItem->resType = (ResType)ConvertUint8ArrayToUint32(defaultCache, INDEX_DEFAULT_OFFSET);
441
442 (void)read(file, defaultCache, INDEX_DEFAULT_OFFSET);
443 idItem->id = ConvertUint8ArrayToUint32(defaultCache, INDEX_DEFAULT_OFFSET);
444
445 (void)read(file, lengthCache, VALUE_LENGTH_OFFSET);
446 idItem->valueLen = (uint16_t)ConvertUint8ArrayToUint32(lengthCache, VALUE_LENGTH_OFFSET);
447 if (idItem->valueLen == 0 || idItem->valueLen > MAX_ITEM_LENGTH) {
448 return MC_FAILURE;
449 }
450
451 idItem->value = (char *)malloc(idItem->valueLen);
452 if (idItem->value == NULL) {
453 return MC_FAILURE;
454 }
455 (void)memset_s(idItem->value, idItem->valueLen, 0, idItem->valueLen);
456 (void)read(file, idItem->value, idItem->valueLen);
457
458 (void)read(file, lengthCache, VALUE_LENGTH_OFFSET);
459 idItem->nameLen = (uint16_t)ConvertUint8ArrayToUint32(lengthCache, VALUE_LENGTH_OFFSET);
460 if (idItem->nameLen == 0 || idItem->nameLen > MAX_ITEM_LENGTH) {
461 free(idItem->value);
462 idItem->value = NULL;
463 return MC_FAILURE;
464 }
465
466 idItem->name = (char *)malloc(idItem->nameLen);
467 if (idItem->name == NULL) {
468 free(idItem->value);
469 idItem->value = NULL;
470 return MC_FAILURE;
471 }
472 (void)memset_s(idItem->name, idItem->nameLen, 0, idItem->nameLen);
473 (void)read(file, idItem->name, idItem->nameLen);
474
475 return MC_SUCCESS;
476 }
477
GetIdHeaderOffsetByLocale(const char * locale,const Key * keys,uint32_t configNum)478 static uint32_t GetIdHeaderOffsetByLocale(const char *locale, const Key *keys, uint32_t configNum)
479 {
480 if (locale == NULL || keys == NULL) {
481 return INVALID_OFFSET;
482 }
483 char *resConfig[LOCALE_ELEMENT_NUM] = {0};
484 int32_t count = 0;
485 if (strchr(locale, '-') != NULL) {
486 (void)Split(locale, "-", resConfig, &count);
487 } else if (strchr(locale, '_') != NULL) {
488 (void)Split(locale, "_", resConfig, &count);
489 } else {
490 resConfig[0] = (char *)locale;
491 count = 1;
492 }
493 return GetIdHeaderOffsetByCount(resConfig, keys, configNum, count);
494 }
495
GetIdHeaderByOffset(int32_t file,uint32_t offset,IdHeader * idHeader)496 static int32_t GetIdHeaderByOffset(int32_t file, uint32_t offset, IdHeader *idHeader)
497 {
498 if (file == -1 || offset == INVALID_OFFSET || idHeader == NULL) {
499 return MC_FAILURE;
500 }
501
502 (void)lseek(file, (int32_t)offset + INDEX_DEFAULT_OFFSET, SEEK_SET); // skip the "IDSS" header
503 idHeader->count = GetDefaultOffsetValue(file);
504 if (idHeader->count == 0 || idHeader->count > MAX_ID_ITEM_NUM) {
505 return MC_FAILURE;
506 }
507 idHeader->idParams = (IdParam *)malloc(sizeof(IdParam) * idHeader->count);
508 if (idHeader->idParams == NULL) {
509 return MC_FAILURE;
510 }
511 for (uint32_t i = 0; i < idHeader->count; i++) {
512 idHeader->idParams[i].id = GetDefaultOffsetValue(file);
513 idHeader->idParams[i].offset = GetDefaultOffsetValue(file);
514 }
515 return MC_SUCCESS;
516 }
517
GetGlobalUtilsImpl(void)518 GlobalUtilsImpl *GetGlobalUtilsImpl(void)
519 {
520 return (GlobalUtilsImpl *)(&g_globalUtilsImpl);
521 }
522