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