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