• 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 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