• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 *   Copyright (C) 2008-2016, International Business Machines
6 *   Corporation and others.  All Rights Reserved.
7 **********************************************************************
8 */
9 
10 #include "unicode/utypes.h"
11 #include "unicode/uspoof.h"
12 #include "unicode/uchar.h"
13 #include "unicode/uniset.h"
14 #include "unicode/utf16.h"
15 #include "utrie2.h"
16 #include "cmemory.h"
17 #include "cstring.h"
18 #include "scriptset.h"
19 #include "umutex.h"
20 #include "udataswp.h"
21 #include "uassert.h"
22 #include "ucln_in.h"
23 #include "uspoof_impl.h"
24 
25 #if !UCONFIG_NO_NORMALIZATION
26 
27 
28 U_NAMESPACE_BEGIN
29 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl)30 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl)
31 
32 SpoofImpl::SpoofImpl(SpoofData *data, UErrorCode& status) {
33     construct(status);
34     fSpoofData = data;
35 }
36 
SpoofImpl(UErrorCode & status)37 SpoofImpl::SpoofImpl(UErrorCode& status) {
38     construct(status);
39 
40     // TODO: Call this method where it is actually needed, instead of in the
41     // constructor, to allow for lazy data loading.  See #12696.
42     fSpoofData = SpoofData::getDefault(status);
43 }
44 
SpoofImpl()45 SpoofImpl::SpoofImpl() {
46     UErrorCode status = U_ZERO_ERROR;
47     construct(status);
48 
49     // TODO: Call this method where it is actually needed, instead of in the
50     // constructor, to allow for lazy data loading.  See #12696.
51     fSpoofData = SpoofData::getDefault(status);
52 }
53 
construct(UErrorCode & status)54 void SpoofImpl::construct(UErrorCode& status) {
55     fChecks = USPOOF_ALL_CHECKS;
56     fSpoofData = NULL;
57     fAllowedCharsSet = NULL;
58     fAllowedLocales = NULL;
59     fRestrictionLevel = USPOOF_HIGHLY_RESTRICTIVE;
60 
61     if (U_FAILURE(status)) { return; }
62 
63     UnicodeSet *allowedCharsSet = new UnicodeSet(0, 0x10ffff);
64     fAllowedCharsSet = allowedCharsSet;
65     fAllowedLocales  = uprv_strdup("");
66     if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) {
67         status = U_MEMORY_ALLOCATION_ERROR;
68         return;
69     }
70     allowedCharsSet->freeze();
71 }
72 
73 
74 // Copy Constructor, used by the user level clone() function.
SpoofImpl(const SpoofImpl & src,UErrorCode & status)75 SpoofImpl::SpoofImpl(const SpoofImpl &src, UErrorCode &status)  :
76         fChecks(USPOOF_ALL_CHECKS), fSpoofData(NULL), fAllowedCharsSet(NULL) ,
77         fAllowedLocales(NULL) {
78     if (U_FAILURE(status)) {
79         return;
80     }
81     fChecks = src.fChecks;
82     if (src.fSpoofData != NULL) {
83         fSpoofData = src.fSpoofData->addReference();
84     }
85     fAllowedCharsSet = src.fAllowedCharsSet->clone();
86     fAllowedLocales = uprv_strdup(src.fAllowedLocales);
87     if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) {
88         status = U_MEMORY_ALLOCATION_ERROR;
89     }
90     fRestrictionLevel = src.fRestrictionLevel;
91 }
92 
~SpoofImpl()93 SpoofImpl::~SpoofImpl() {
94     if (fSpoofData != NULL) {
95         fSpoofData->removeReference();   // Will delete if refCount goes to zero.
96     }
97     delete fAllowedCharsSet;
98     uprv_free((void *)fAllowedLocales);
99 }
100 
101 //  Cast this instance as a USpoofChecker for the C API.
asUSpoofChecker()102 USpoofChecker *SpoofImpl::asUSpoofChecker() {
103     return exportForC();
104 }
105 
106 //
107 //  Incoming parameter check on Status and the SpoofChecker object
108 //    received from the C API.
109 //
validateThis(const USpoofChecker * sc,UErrorCode & status)110 const SpoofImpl *SpoofImpl::validateThis(const USpoofChecker *sc, UErrorCode &status) {
111     auto* This = validate(sc, status);
112     if (U_FAILURE(status)) {
113         return NULL;
114     }
115     if (This->fSpoofData != NULL && !This->fSpoofData->validateDataVersion(status)) {
116         return NULL;
117     }
118     return This;
119 }
120 
validateThis(USpoofChecker * sc,UErrorCode & status)121 SpoofImpl *SpoofImpl::validateThis(USpoofChecker *sc, UErrorCode &status) {
122     return const_cast<SpoofImpl *>
123         (SpoofImpl::validateThis(const_cast<const USpoofChecker *>(sc), status));
124 }
125 
126 
setAllowedLocales(const char * localesList,UErrorCode & status)127 void SpoofImpl::setAllowedLocales(const char *localesList, UErrorCode &status) {
128     UnicodeSet    allowedChars;
129     UnicodeSet    *tmpSet = NULL;
130     const char    *locStart = localesList;
131     const char    *locEnd = NULL;
132     const char    *localesListEnd = localesList + uprv_strlen(localesList);
133     int32_t        localeListCount = 0;   // Number of locales provided by caller.
134 
135     // Loop runs once per locale from the localesList, a comma separated list of locales.
136     do {
137         locEnd = uprv_strchr(locStart, ',');
138         if (locEnd == NULL) {
139             locEnd = localesListEnd;
140         }
141         while (*locStart == ' ') {
142             locStart++;
143         }
144         const char *trimmedEnd = locEnd-1;
145         while (trimmedEnd > locStart && *trimmedEnd == ' ') {
146             trimmedEnd--;
147         }
148         if (trimmedEnd <= locStart) {
149             break;
150         }
151         const char *locale = uprv_strndup(locStart, (int32_t)(trimmedEnd + 1 - locStart));
152         localeListCount++;
153 
154         // We have one locale from the locales list.
155         // Add the script chars for this locale to the accumulating set of allowed chars.
156         // If the locale is no good, we will be notified back via status.
157         addScriptChars(locale, &allowedChars, status);
158         uprv_free((void *)locale);
159         if (U_FAILURE(status)) {
160             break;
161         }
162         locStart = locEnd + 1;
163     } while (locStart < localesListEnd);
164 
165     // If our caller provided an empty list of locales, we disable the allowed characters checking
166     if (localeListCount == 0) {
167         uprv_free((void *)fAllowedLocales);
168         fAllowedLocales = uprv_strdup("");
169         tmpSet = new UnicodeSet(0, 0x10ffff);
170         if (fAllowedLocales == NULL || tmpSet == NULL) {
171             status = U_MEMORY_ALLOCATION_ERROR;
172             return;
173         }
174         tmpSet->freeze();
175         delete fAllowedCharsSet;
176         fAllowedCharsSet = tmpSet;
177         fChecks &= ~USPOOF_CHAR_LIMIT;
178         return;
179     }
180 
181 
182     // Add all common and inherited characters to the set of allowed chars.
183     UnicodeSet tempSet;
184     tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_COMMON, status);
185     allowedChars.addAll(tempSet);
186     tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_INHERITED, status);
187     allowedChars.addAll(tempSet);
188 
189     // If anything went wrong, we bail out without changing
190     // the state of the spoof checker.
191     if (U_FAILURE(status)) {
192         return;
193     }
194 
195     // Store the updated spoof checker state.
196     tmpSet = allowedChars.clone();
197     const char *tmpLocalesList = uprv_strdup(localesList);
198     if (tmpSet == NULL || tmpLocalesList == NULL) {
199         status = U_MEMORY_ALLOCATION_ERROR;
200         return;
201     }
202     uprv_free((void *)fAllowedLocales);
203     fAllowedLocales = tmpLocalesList;
204     tmpSet->freeze();
205     delete fAllowedCharsSet;
206     fAllowedCharsSet = tmpSet;
207     fChecks |= USPOOF_CHAR_LIMIT;
208 }
209 
210 
getAllowedLocales(UErrorCode &)211 const char * SpoofImpl::getAllowedLocales(UErrorCode &/*status*/) {
212     return fAllowedLocales;
213 }
214 
215 
216 // Given a locale (a language), add all the characters from all of the scripts used with that language
217 // to the allowedChars UnicodeSet
218 
addScriptChars(const char * locale,UnicodeSet * allowedChars,UErrorCode & status)219 void SpoofImpl::addScriptChars(const char *locale, UnicodeSet *allowedChars, UErrorCode &status) {
220     UScriptCode scripts[30];
221 
222     int32_t numScripts = uscript_getCode(locale, scripts, UPRV_LENGTHOF(scripts), &status);
223     if (U_FAILURE(status)) {
224         return;
225     }
226     if (status == U_USING_DEFAULT_WARNING) {
227         status = U_ILLEGAL_ARGUMENT_ERROR;
228         return;
229     }
230     UnicodeSet tmpSet;
231     int32_t    i;
232     for (i=0; i<numScripts; i++) {
233         tmpSet.applyIntPropertyValue(UCHAR_SCRIPT, scripts[i], status);
234         allowedChars->addAll(tmpSet);
235     }
236 }
237 
238 // Computes the augmented script set for a code point, according to UTS 39 section 5.1.
getAugmentedScriptSet(UChar32 codePoint,ScriptSet & result,UErrorCode & status)239 void SpoofImpl::getAugmentedScriptSet(UChar32 codePoint, ScriptSet& result, UErrorCode& status) {
240     result.resetAll();
241     result.setScriptExtensions(codePoint, status);
242     if (U_FAILURE(status)) { return; }
243 
244     // Section 5.1 step 1
245     if (result.test(USCRIPT_HAN, status)) {
246         result.set(USCRIPT_HAN_WITH_BOPOMOFO, status);
247         result.set(USCRIPT_JAPANESE, status);
248         result.set(USCRIPT_KOREAN, status);
249     }
250     if (result.test(USCRIPT_HIRAGANA, status)) {
251         result.set(USCRIPT_JAPANESE, status);
252     }
253     if (result.test(USCRIPT_KATAKANA, status)) {
254         result.set(USCRIPT_JAPANESE, status);
255     }
256     if (result.test(USCRIPT_HANGUL, status)) {
257         result.set(USCRIPT_KOREAN, status);
258     }
259     if (result.test(USCRIPT_BOPOMOFO, status)) {
260         result.set(USCRIPT_HAN_WITH_BOPOMOFO, status);
261     }
262 
263     // Section 5.1 step 2
264     if (result.test(USCRIPT_COMMON, status) || result.test(USCRIPT_INHERITED, status)) {
265         result.setAll();
266     }
267 }
268 
269 // Computes the resolved script set for a string, according to UTS 39 section 5.1.
getResolvedScriptSet(const UnicodeString & input,ScriptSet & result,UErrorCode & status) const270 void SpoofImpl::getResolvedScriptSet(const UnicodeString& input, ScriptSet& result, UErrorCode& status) const {
271     getResolvedScriptSetWithout(input, USCRIPT_CODE_LIMIT, result, status);
272 }
273 
274 // Computes the resolved script set for a string, omitting characters having the specified script.
275 // If USCRIPT_CODE_LIMIT is passed as the second argument, all characters are included.
getResolvedScriptSetWithout(const UnicodeString & input,UScriptCode script,ScriptSet & result,UErrorCode & status) const276 void SpoofImpl::getResolvedScriptSetWithout(const UnicodeString& input, UScriptCode script, ScriptSet& result, UErrorCode& status) const {
277     result.setAll();
278 
279     ScriptSet temp;
280     UChar32 codePoint;
281     for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) {
282         codePoint = input.char32At(i);
283 
284         // Compute the augmented script set for the character
285         getAugmentedScriptSet(codePoint, temp, status);
286         if (U_FAILURE(status)) { return; }
287 
288         // Intersect the augmented script set with the resolved script set, but only if the character doesn't
289         // have the script specified in the function call
290         if (script == USCRIPT_CODE_LIMIT || !temp.test(script, status)) {
291             result.intersect(temp);
292         }
293     }
294 }
295 
296 // Computes the set of numerics for a string, according to UTS 39 section 5.3.
getNumerics(const UnicodeString & input,UnicodeSet & result,UErrorCode &) const297 void SpoofImpl::getNumerics(const UnicodeString& input, UnicodeSet& result, UErrorCode& /*status*/) const {
298     result.clear();
299 
300     UChar32 codePoint;
301     for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) {
302         codePoint = input.char32At(i);
303 
304         // Store a representative character for each kind of decimal digit
305         if (u_charType(codePoint) == U_DECIMAL_DIGIT_NUMBER) {
306             // Store the zero character as a representative for comparison.
307             // Unicode guarantees it is codePoint - value
308             result.add(codePoint - (UChar32)u_getNumericValue(codePoint));
309         }
310     }
311 }
312 
313 // Computes the restriction level of a string, according to UTS 39 section 5.2.
getRestrictionLevel(const UnicodeString & input,UErrorCode & status) const314 URestrictionLevel SpoofImpl::getRestrictionLevel(const UnicodeString& input, UErrorCode& status) const {
315     // Section 5.2 step 1:
316     if (!fAllowedCharsSet->containsAll(input)) {
317         return USPOOF_UNRESTRICTIVE;
318     }
319 
320     // Section 5.2 step 2
321     // Java use a static UnicodeSet for this test.  In C++, avoid the static variable
322     // and just do a simple for loop.
323     UBool allASCII = TRUE;
324     for (int32_t i=0, length=input.length(); i<length; i++) {
325         if (input.charAt(i) > 0x7f) {
326             allASCII = FALSE;
327             break;
328         }
329     }
330     if (allASCII) {
331         return USPOOF_ASCII;
332     }
333 
334     // Section 5.2 steps 3:
335     ScriptSet resolvedScriptSet;
336     getResolvedScriptSet(input, resolvedScriptSet, status);
337     if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; }
338 
339     // Section 5.2 step 4:
340     if (!resolvedScriptSet.isEmpty()) {
341         return USPOOF_SINGLE_SCRIPT_RESTRICTIVE;
342     }
343 
344     // Section 5.2 step 5:
345     ScriptSet resolvedNoLatn;
346     getResolvedScriptSetWithout(input, USCRIPT_LATIN, resolvedNoLatn, status);
347     if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; }
348 
349     // Section 5.2 step 6:
350     if (resolvedNoLatn.test(USCRIPT_HAN_WITH_BOPOMOFO, status)
351             || resolvedNoLatn.test(USCRIPT_JAPANESE, status)
352             || resolvedNoLatn.test(USCRIPT_KOREAN, status)) {
353         return USPOOF_HIGHLY_RESTRICTIVE;
354     }
355 
356     // Section 5.2 step 7:
357     if (!resolvedNoLatn.isEmpty()
358             && !resolvedNoLatn.test(USCRIPT_CYRILLIC, status)
359             && !resolvedNoLatn.test(USCRIPT_GREEK, status)
360             && !resolvedNoLatn.test(USCRIPT_CHEROKEE, status)) {
361         return USPOOF_MODERATELY_RESTRICTIVE;
362     }
363 
364     // Section 5.2 step 8:
365     return USPOOF_MINIMALLY_RESTRICTIVE;
366 }
367 
findHiddenOverlay(const UnicodeString & input,UErrorCode &) const368 int32_t SpoofImpl::findHiddenOverlay(const UnicodeString& input, UErrorCode&) const {
369     bool sawLeadCharacter = false;
370     for (int32_t i=0; i<input.length();) {
371         UChar32 cp = input.char32At(i);
372         if (sawLeadCharacter && cp == 0x0307) {
373             return i;
374         }
375         uint8_t combiningClass = u_getCombiningClass(cp);
376         // Skip over characters except for those with combining class 0 (non-combining characters) or with
377         // combining class 230 (same class as U+0307)
378         U_ASSERT(u_getCombiningClass(0x0307) == 230);
379         if (combiningClass == 0 || combiningClass == 230) {
380             sawLeadCharacter = isIllegalCombiningDotLeadCharacter(cp);
381         }
382         i += U16_LENGTH(cp);
383     }
384     return -1;
385 }
386 
isIllegalCombiningDotLeadCharacterNoLookup(UChar32 cp)387 static inline bool isIllegalCombiningDotLeadCharacterNoLookup(UChar32 cp) {
388     return cp == u'i' || cp == u'j' || cp == u'ı' || cp == u'ȷ' || cp == u'l' ||
389            u_hasBinaryProperty(cp, UCHAR_SOFT_DOTTED);
390 }
391 
isIllegalCombiningDotLeadCharacter(UChar32 cp) const392 bool SpoofImpl::isIllegalCombiningDotLeadCharacter(UChar32 cp) const {
393     if (isIllegalCombiningDotLeadCharacterNoLookup(cp)) {
394         return true;
395     }
396     UnicodeString skelStr;
397     fSpoofData->confusableLookup(cp, skelStr);
398     UChar32 finalCp = skelStr.char32At(skelStr.moveIndex32(skelStr.length(), -1));
399     if (finalCp != cp && isIllegalCombiningDotLeadCharacterNoLookup(finalCp)) {
400         return true;
401     }
402     return false;
403 }
404 
405 
406 
407 // Convert a text format hex number.  Utility function used by builder code.  Static.
408 // Input: UChar *string text.  Output: a UChar32
409 // Input has been pre-checked, and will have no non-hex chars.
410 // The number must fall in the code point range of 0..0x10ffff
411 // Static Function.
ScanHex(const UChar * s,int32_t start,int32_t limit,UErrorCode & status)412 UChar32 SpoofImpl::ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status) {
413     if (U_FAILURE(status)) {
414         return 0;
415     }
416     U_ASSERT(limit-start > 0);
417     uint32_t val = 0;
418     int i;
419     for (i=start; i<limit; i++) {
420         int digitVal = s[i] - 0x30;
421         if (digitVal>9) {
422             digitVal = 0xa + (s[i] - 0x41);  // Upper Case 'A'
423         }
424         if (digitVal>15) {
425             digitVal = 0xa + (s[i] - 0x61);  // Lower Case 'a'
426         }
427         U_ASSERT(digitVal <= 0xf);
428         val <<= 4;
429         val += digitVal;
430     }
431     if (val > 0x10ffff) {
432         status = U_PARSE_ERROR;
433         val = 0;
434     }
435     return (UChar32)val;
436 }
437 
438 
439 //-----------------------------------------
440 //
441 //   class CheckResult Implementation
442 //
443 //-----------------------------------------
444 
CheckResult()445 CheckResult::CheckResult() {
446     clear();
447 }
448 
asUSpoofCheckResult()449 USpoofCheckResult* CheckResult::asUSpoofCheckResult() {
450     return exportForC();
451 }
452 
453 //
454 //  Incoming parameter check on Status and the CheckResult object
455 //    received from the C API.
456 //
validateThis(const USpoofCheckResult * ptr,UErrorCode & status)457 const CheckResult* CheckResult::validateThis(const USpoofCheckResult *ptr, UErrorCode &status) {
458     return validate(ptr, status);
459 }
460 
validateThis(USpoofCheckResult * ptr,UErrorCode & status)461 CheckResult* CheckResult::validateThis(USpoofCheckResult *ptr, UErrorCode &status) {
462     return validate(ptr, status);
463 }
464 
clear()465 void CheckResult::clear() {
466     fChecks = 0;
467     fNumerics.clear();
468     fRestrictionLevel = USPOOF_UNDEFINED_RESTRICTIVE;
469 }
470 
toCombinedBitmask(int32_t enabledChecks)471 int32_t CheckResult::toCombinedBitmask(int32_t enabledChecks) {
472     if ((enabledChecks & USPOOF_AUX_INFO) != 0 && fRestrictionLevel != USPOOF_UNDEFINED_RESTRICTIVE) {
473         return fChecks | fRestrictionLevel;
474     } else {
475         return fChecks;
476     }
477 }
478 
~CheckResult()479 CheckResult::~CheckResult() {
480 }
481 
482 //----------------------------------------------------------------------------------------------
483 //
484 //   class SpoofData Implementation
485 //
486 //----------------------------------------------------------------------------------------------
487 
488 
validateDataVersion(UErrorCode & status) const489 UBool SpoofData::validateDataVersion(UErrorCode &status) const {
490     if (U_FAILURE(status) ||
491         fRawData == NULL ||
492         fRawData->fMagic != USPOOF_MAGIC ||
493         fRawData->fFormatVersion[0] != USPOOF_CONFUSABLE_DATA_FORMAT_VERSION ||
494         fRawData->fFormatVersion[1] != 0 ||
495         fRawData->fFormatVersion[2] != 0 ||
496         fRawData->fFormatVersion[3] != 0) {
497             status = U_INVALID_FORMAT_ERROR;
498             return FALSE;
499     }
500     return TRUE;
501 }
502 
503 static UBool U_CALLCONV
spoofDataIsAcceptable(void * context,const char *,const char *,const UDataInfo * pInfo)504 spoofDataIsAcceptable(void *context,
505                         const char * /* type */, const char * /*name*/,
506                         const UDataInfo *pInfo) {
507     if(
508         pInfo->size >= 20 &&
509         pInfo->isBigEndian == U_IS_BIG_ENDIAN &&
510         pInfo->charsetFamily == U_CHARSET_FAMILY &&
511         pInfo->dataFormat[0] == 0x43 &&  // dataFormat="Cfu "
512         pInfo->dataFormat[1] == 0x66 &&
513         pInfo->dataFormat[2] == 0x75 &&
514         pInfo->dataFormat[3] == 0x20 &&
515         pInfo->formatVersion[0] == USPOOF_CONFUSABLE_DATA_FORMAT_VERSION
516     ) {
517         UVersionInfo *version = static_cast<UVersionInfo *>(context);
518         if(version != NULL) {
519             uprv_memcpy(version, pInfo->dataVersion, 4);
520         }
521         return TRUE;
522     } else {
523         return FALSE;
524     }
525 }
526 
527 //  Methods for the loading of the default confusables data file.  The confusable
528 //  data is loaded only when it is needed.
529 //
530 //  SpoofData::getDefault() - Return the default confusables data, and call the
531 //                            initOnce() if it is not available.  Adds a reference
532 //                            to the SpoofData that the caller is responsible for
533 //                            decrementing when they are done with the data.
534 //
535 //  uspoof_loadDefaultData - Called once, from initOnce().  The resulting SpoofData
536 //                           is shared by all spoof checkers using the default data.
537 //
538 //  uspoof_cleanupDefaultData - Called during cleanup.
539 //
540 
541 static UInitOnce gSpoofInitDefaultOnce = U_INITONCE_INITIALIZER;
542 static SpoofData* gDefaultSpoofData;
543 
544 static UBool U_CALLCONV
uspoof_cleanupDefaultData(void)545 uspoof_cleanupDefaultData(void) {
546     if (gDefaultSpoofData) {
547         // Will delete, assuming all user-level spoof checkers were closed.
548         gDefaultSpoofData->removeReference();
549         gDefaultSpoofData = nullptr;
550         gSpoofInitDefaultOnce.reset();
551     }
552     return TRUE;
553 }
554 
uspoof_loadDefaultData(UErrorCode & status)555 static void U_CALLCONV uspoof_loadDefaultData(UErrorCode& status) {
556     UDataMemory *udm = udata_openChoice(nullptr, "cfu", "confusables",
557                                         spoofDataIsAcceptable,
558                                         nullptr,       // context, would receive dataVersion if supplied.
559                                         &status);
560     if (U_FAILURE(status)) { return; }
561     gDefaultSpoofData = new SpoofData(udm, status);
562     if (U_FAILURE(status)) {
563         delete gDefaultSpoofData;
564         gDefaultSpoofData = nullptr;
565         return;
566     }
567     if (gDefaultSpoofData == nullptr) {
568         status = U_MEMORY_ALLOCATION_ERROR;
569         return;
570     }
571     ucln_i18n_registerCleanup(UCLN_I18N_SPOOFDATA, uspoof_cleanupDefaultData);
572 }
573 
getDefault(UErrorCode & status)574 SpoofData* SpoofData::getDefault(UErrorCode& status) {
575     umtx_initOnce(gSpoofInitDefaultOnce, &uspoof_loadDefaultData, status);
576     if (U_FAILURE(status)) { return NULL; }
577     gDefaultSpoofData->addReference();
578     return gDefaultSpoofData;
579 }
580 
581 
582 
SpoofData(UDataMemory * udm,UErrorCode & status)583 SpoofData::SpoofData(UDataMemory *udm, UErrorCode &status)
584 {
585     reset();
586     if (U_FAILURE(status)) {
587         return;
588     }
589     fUDM = udm;
590     // fRawData is non-const because it may be constructed by the data builder.
591     fRawData = reinterpret_cast<SpoofDataHeader *>(
592             const_cast<void *>(udata_getMemory(udm)));
593     validateDataVersion(status);
594     initPtrs(status);
595 }
596 
597 
SpoofData(const void * data,int32_t length,UErrorCode & status)598 SpoofData::SpoofData(const void *data, int32_t length, UErrorCode &status)
599 {
600     reset();
601     if (U_FAILURE(status)) {
602         return;
603     }
604     if ((size_t)length < sizeof(SpoofDataHeader)) {
605         status = U_INVALID_FORMAT_ERROR;
606         return;
607     }
608     if (data == NULL) {
609         status = U_ILLEGAL_ARGUMENT_ERROR;
610         return;
611     }
612     void *ncData = const_cast<void *>(data);
613     fRawData = static_cast<SpoofDataHeader *>(ncData);
614     if (length < fRawData->fLength) {
615         status = U_INVALID_FORMAT_ERROR;
616         return;
617     }
618     validateDataVersion(status);
619     initPtrs(status);
620 }
621 
622 
623 // Spoof Data constructor for use from data builder.
624 //   Initializes a new, empty data area that will be populated later.
SpoofData(UErrorCode & status)625 SpoofData::SpoofData(UErrorCode &status) {
626     reset();
627     if (U_FAILURE(status)) {
628         return;
629     }
630     fDataOwned = true;
631 
632     // The spoof header should already be sized to be a multiple of 16 bytes.
633     // Just in case it's not, round it up.
634     uint32_t initialSize = (sizeof(SpoofDataHeader) + 15) & ~15;
635     U_ASSERT(initialSize == sizeof(SpoofDataHeader));
636 
637     fRawData = static_cast<SpoofDataHeader *>(uprv_malloc(initialSize));
638     fMemLimit = initialSize;
639     if (fRawData == NULL) {
640         status = U_MEMORY_ALLOCATION_ERROR;
641         return;
642     }
643     uprv_memset(fRawData, 0, initialSize);
644 
645     fRawData->fMagic = USPOOF_MAGIC;
646     fRawData->fFormatVersion[0] = USPOOF_CONFUSABLE_DATA_FORMAT_VERSION;
647     fRawData->fFormatVersion[1] = 0;
648     fRawData->fFormatVersion[2] = 0;
649     fRawData->fFormatVersion[3] = 0;
650     initPtrs(status);
651 }
652 
653 // reset() - initialize all fields.
654 //           Should be updated if any new fields are added.
655 //           Called by constructors to put things in a known initial state.
reset()656 void SpoofData::reset() {
657    fRawData = NULL;
658    fDataOwned = FALSE;
659    fUDM      = NULL;
660    fMemLimit = 0;
661    fRefCount = 1;
662    fCFUKeys = NULL;
663    fCFUValues = NULL;
664    fCFUStrings = NULL;
665 }
666 
667 
668 //  SpoofData::initPtrs()
669 //            Initialize the pointers to the various sections of the raw data.
670 //
671 //            This function is used both during the Trie building process (multiple
672 //            times, as the individual data sections are added), and
673 //            during the opening of a Spoof Checker from prebuilt data.
674 //
675 //            The pointers for non-existent data sections (identified by an offset of 0)
676 //            are set to NULL.
677 //
678 //            Note:  During building the data, adding each new data section
679 //            reallocs the raw data area, which likely relocates it, which
680 //            in turn requires reinitializing all of the pointers into it, hence
681 //            multiple calls to this function during building.
682 //
initPtrs(UErrorCode & status)683 void SpoofData::initPtrs(UErrorCode &status) {
684     fCFUKeys = NULL;
685     fCFUValues = NULL;
686     fCFUStrings = NULL;
687     if (U_FAILURE(status)) {
688         return;
689     }
690     if (fRawData->fCFUKeys != 0) {
691         fCFUKeys = (int32_t *)((char *)fRawData + fRawData->fCFUKeys);
692     }
693     if (fRawData->fCFUStringIndex != 0) {
694         fCFUValues = (uint16_t *)((char *)fRawData + fRawData->fCFUStringIndex);
695     }
696     if (fRawData->fCFUStringTable != 0) {
697         fCFUStrings = (UChar *)((char *)fRawData + fRawData->fCFUStringTable);
698     }
699 }
700 
701 
~SpoofData()702 SpoofData::~SpoofData() {
703     if (fDataOwned) {
704         uprv_free(fRawData);
705     }
706     fRawData = NULL;
707     if (fUDM != NULL) {
708         udata_close(fUDM);
709     }
710     fUDM = NULL;
711 }
712 
713 
removeReference()714 void SpoofData::removeReference() {
715     if (umtx_atomic_dec(&fRefCount) == 0) {
716         delete this;
717     }
718 }
719 
720 
addReference()721 SpoofData *SpoofData::addReference() {
722     umtx_atomic_inc(&fRefCount);
723     return this;
724 }
725 
726 
reserveSpace(int32_t numBytes,UErrorCode & status)727 void *SpoofData::reserveSpace(int32_t numBytes,  UErrorCode &status) {
728     if (U_FAILURE(status)) {
729         return NULL;
730     }
731     if (!fDataOwned) {
732         UPRV_UNREACHABLE;
733     }
734 
735     numBytes = (numBytes + 15) & ~15;   // Round up to a multiple of 16
736     uint32_t returnOffset = fMemLimit;
737     fMemLimit += numBytes;
738     fRawData = static_cast<SpoofDataHeader *>(uprv_realloc(fRawData, fMemLimit));
739     fRawData->fLength = fMemLimit;
740     uprv_memset((char *)fRawData + returnOffset, 0, numBytes);
741     initPtrs(status);
742     return (char *)fRawData + returnOffset;
743 }
744 
serialize(void * buf,int32_t capacity,UErrorCode & status) const745 int32_t SpoofData::serialize(void *buf, int32_t capacity, UErrorCode &status) const {
746     int32_t dataSize = fRawData->fLength;
747     if (capacity < dataSize) {
748         status = U_BUFFER_OVERFLOW_ERROR;
749         return dataSize;
750     }
751     uprv_memcpy(buf, fRawData, dataSize);
752     return dataSize;
753 }
754 
size() const755 int32_t SpoofData::size() const {
756     return fRawData->fLength;
757 }
758 
759 //-------------------------------
760 //
761 // Front-end APIs for SpoofData
762 //
763 //-------------------------------
764 
confusableLookup(UChar32 inChar,UnicodeString & dest) const765 int32_t SpoofData::confusableLookup(UChar32 inChar, UnicodeString &dest) const {
766     // Perform a binary search.
767     // [lo, hi), i.e lo is inclusive, hi is exclusive.
768     // The result after the loop will be in lo.
769     int32_t lo = 0;
770     int32_t hi = length();
771     do {
772         int32_t mid = (lo + hi) / 2;
773         if (codePointAt(mid) > inChar) {
774             hi = mid;
775         } else if (codePointAt(mid) < inChar) {
776             lo = mid;
777         } else {
778             // Found result.  Break early.
779             lo = mid;
780             break;
781         }
782     } while (hi - lo > 1);
783 
784     // Did we find an entry?  If not, the char maps to itself.
785     if (codePointAt(lo) != inChar) {
786         dest.append(inChar);
787         return 1;
788     }
789 
790     // Add the element to the string builder and return.
791     return appendValueTo(lo, dest);
792 }
793 
length() const794 int32_t SpoofData::length() const {
795     return fRawData->fCFUKeysSize;
796 }
797 
codePointAt(int32_t index) const798 UChar32 SpoofData::codePointAt(int32_t index) const {
799     return ConfusableDataUtils::keyToCodePoint(fCFUKeys[index]);
800 }
801 
appendValueTo(int32_t index,UnicodeString & dest) const802 int32_t SpoofData::appendValueTo(int32_t index, UnicodeString& dest) const {
803     int32_t stringLength = ConfusableDataUtils::keyToLength(fCFUKeys[index]);
804 
805     // Value is either a char (for strings of length 1) or
806     // an index into the string table (for longer strings)
807     uint16_t value = fCFUValues[index];
808     if (stringLength == 1) {
809         dest.append((UChar)value);
810     } else {
811         dest.append(fCFUStrings + value, stringLength);
812     }
813 
814     return stringLength;
815 }
816 
817 
818 U_NAMESPACE_END
819 
820 U_NAMESPACE_USE
821 
822 //-----------------------------------------------------------------------------
823 //
824 //  uspoof_swap   -  byte swap and char encoding swap of spoof data
825 //
826 //-----------------------------------------------------------------------------
827 U_CAPI int32_t U_EXPORT2
uspoof_swap(const UDataSwapper * ds,const void * inData,int32_t length,void * outData,UErrorCode * status)828 uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData,
829            UErrorCode *status) {
830 
831     if (status == NULL || U_FAILURE(*status)) {
832         return 0;
833     }
834     if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) {
835         *status=U_ILLEGAL_ARGUMENT_ERROR;
836         return 0;
837     }
838 
839     //
840     //  Check that the data header is for spoof data.
841     //    (Header contents are defined in gencfu.cpp)
842     //
843     const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4);
844     if(!(  pInfo->dataFormat[0]==0x43 &&   /* dataFormat="Cfu " */
845            pInfo->dataFormat[1]==0x66 &&
846            pInfo->dataFormat[2]==0x75 &&
847            pInfo->dataFormat[3]==0x20 &&
848            pInfo->formatVersion[0]==USPOOF_CONFUSABLE_DATA_FORMAT_VERSION &&
849            pInfo->formatVersion[1]==0 &&
850            pInfo->formatVersion[2]==0 &&
851            pInfo->formatVersion[3]==0  )) {
852         udata_printError(ds, "uspoof_swap(): data format %02x.%02x.%02x.%02x "
853                              "(format version %02x %02x %02x %02x) is not recognized\n",
854                          pInfo->dataFormat[0], pInfo->dataFormat[1],
855                          pInfo->dataFormat[2], pInfo->dataFormat[3],
856                          pInfo->formatVersion[0], pInfo->formatVersion[1],
857                          pInfo->formatVersion[2], pInfo->formatVersion[3]);
858         *status=U_UNSUPPORTED_ERROR;
859         return 0;
860     }
861 
862     //
863     // Swap the data header.  (This is the generic ICU Data Header, not the uspoof Specific
864     //                         header).  This swap also conveniently gets us
865     //                         the size of the ICU d.h., which lets us locate the start
866     //                         of the uspoof specific data.
867     //
868     int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status);
869 
870 
871     //
872     // Get the Spoof Data Header, and check that it appears to be OK.
873     //
874     //
875     const uint8_t   *inBytes =(const uint8_t *)inData+headerSize;
876     SpoofDataHeader *spoofDH = (SpoofDataHeader *)inBytes;
877     if (ds->readUInt32(spoofDH->fMagic)   != USPOOF_MAGIC ||
878         ds->readUInt32(spoofDH->fLength)  <  sizeof(SpoofDataHeader))
879     {
880         udata_printError(ds, "uspoof_swap(): Spoof Data header is invalid.\n");
881         *status=U_UNSUPPORTED_ERROR;
882         return 0;
883     }
884 
885     //
886     // Prefight operation?  Just return the size
887     //
888     int32_t spoofDataLength = ds->readUInt32(spoofDH->fLength);
889     int32_t totalSize = headerSize + spoofDataLength;
890     if (length < 0) {
891         return totalSize;
892     }
893 
894     //
895     // Check that length passed in is consistent with length from Spoof data header.
896     //
897     if (length < totalSize) {
898         udata_printError(ds, "uspoof_swap(): too few bytes (%d after ICU Data header) for spoof data.\n",
899                             spoofDataLength);
900         *status=U_INDEX_OUTOFBOUNDS_ERROR;
901         return 0;
902         }
903 
904 
905     //
906     // Swap the Data.  Do the data itself first, then the Spoof Data Header, because
907     //                 we need to reference the header to locate the data, and an
908     //                 inplace swap of the header leaves it unusable.
909     //
910     uint8_t          *outBytes = (uint8_t *)outData + headerSize;
911     SpoofDataHeader  *outputDH = (SpoofDataHeader *)outBytes;
912 
913     int32_t   sectionStart;
914     int32_t   sectionLength;
915 
916     //
917     // If not swapping in place, zero out the output buffer before starting.
918     //    Gaps may exist between the individual sections, and these must be zeroed in
919     //    the output buffer.  The simplest way to do that is to just zero the whole thing.
920     //
921     if (inBytes != outBytes) {
922         uprv_memset(outBytes, 0, spoofDataLength);
923     }
924 
925     // Confusables Keys Section   (fCFUKeys)
926     sectionStart  = ds->readUInt32(spoofDH->fCFUKeys);
927     sectionLength = ds->readUInt32(spoofDH->fCFUKeysSize) * 4;
928     ds->swapArray32(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
929 
930     // String Index Section
931     sectionStart  = ds->readUInt32(spoofDH->fCFUStringIndex);
932     sectionLength = ds->readUInt32(spoofDH->fCFUStringIndexSize) * 2;
933     ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
934 
935     // String Table Section
936     sectionStart  = ds->readUInt32(spoofDH->fCFUStringTable);
937     sectionLength = ds->readUInt32(spoofDH->fCFUStringTableLen) * 2;
938     ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status);
939 
940     // And, last, swap the header itself.
941     //   int32_t   fMagic             // swap this
942     //   uint8_t   fFormatVersion[4]  // Do not swap this, just copy
943     //   int32_t   fLength and all the rest       // Swap the rest, all is 32 bit stuff.
944     //
945     uint32_t magic = ds->readUInt32(spoofDH->fMagic);
946     ds->writeUInt32((uint32_t *)&outputDH->fMagic, magic);
947 
948     if (outputDH->fFormatVersion != spoofDH->fFormatVersion) {
949         uprv_memcpy(outputDH->fFormatVersion, spoofDH->fFormatVersion, sizeof(spoofDH->fFormatVersion));
950     }
951     // swap starting at fLength
952     ds->swapArray32(ds, &spoofDH->fLength, sizeof(SpoofDataHeader)-8 /* minus magic and fFormatVersion[4] */, &outputDH->fLength, status);
953 
954     return totalSize;
955 }
956 
957 #endif
958 
959 
960