| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ********************************************************************** |
| * Copyright (C) 2008-2016, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ********************************************************************** |
| */ |
| |
| #if defined(STARBOARD) |
| #include "starboard/client_porting/poem/assert_poem.h" |
| #include "starboard/client_porting/poem/string_poem.h" |
| #endif // defined(STARBOARD) |
| #include "unicode/utypes.h" |
| #include "unicode/uspoof.h" |
| #include "unicode/uchar.h" |
| #include "unicode/uniset.h" |
| #include "unicode/utf16.h" |
| #include "utrie2.h" |
| #include "cmemory.h" |
| #include "cstring.h" |
| #include "scriptset.h" |
| #include "umutex.h" |
| #include "udataswp.h" |
| #include "uassert.h" |
| #include "ucln_in.h" |
| #include "uspoof_impl.h" |
| |
| #if !UCONFIG_NO_NORMALIZATION |
| |
| |
| U_NAMESPACE_BEGIN |
| |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SpoofImpl) |
| |
| SpoofImpl::SpoofImpl(SpoofData *data, UErrorCode& status) { |
| construct(status); |
| fSpoofData = data; |
| } |
| |
| SpoofImpl::SpoofImpl(UErrorCode& status) { |
| construct(status); |
| |
| // TODO: Call this method where it is actually needed, instead of in the |
| // constructor, to allow for lazy data loading. See #12696. |
| fSpoofData = SpoofData::getDefault(status); |
| } |
| |
| SpoofImpl::SpoofImpl() { |
| UErrorCode status = U_ZERO_ERROR; |
| construct(status); |
| |
| // TODO: Call this method where it is actually needed, instead of in the |
| // constructor, to allow for lazy data loading. See #12696. |
| fSpoofData = SpoofData::getDefault(status); |
| } |
| |
| void SpoofImpl::construct(UErrorCode& status) { |
| fChecks = USPOOF_ALL_CHECKS; |
| fSpoofData = NULL; |
| fAllowedCharsSet = NULL; |
| fAllowedLocales = NULL; |
| fRestrictionLevel = USPOOF_HIGHLY_RESTRICTIVE; |
| |
| if (U_FAILURE(status)) { return; } |
| |
| UnicodeSet *allowedCharsSet = new UnicodeSet(0, 0x10ffff); |
| fAllowedCharsSet = allowedCharsSet; |
| fAllowedLocales = uprv_strdup(""); |
| if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| allowedCharsSet->freeze(); |
| } |
| |
| |
| // Copy Constructor, used by the user level clone() function. |
| SpoofImpl::SpoofImpl(const SpoofImpl &src, UErrorCode &status) : |
| fChecks(USPOOF_ALL_CHECKS), fSpoofData(NULL), fAllowedCharsSet(NULL) , |
| fAllowedLocales(NULL) { |
| if (U_FAILURE(status)) { |
| return; |
| } |
| fChecks = src.fChecks; |
| if (src.fSpoofData != NULL) { |
| fSpoofData = src.fSpoofData->addReference(); |
| } |
| fAllowedCharsSet = src.fAllowedCharsSet->clone(); |
| fAllowedLocales = uprv_strdup(src.fAllowedLocales); |
| if (fAllowedCharsSet == NULL || fAllowedLocales == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| } |
| fRestrictionLevel = src.fRestrictionLevel; |
| } |
| |
| SpoofImpl::~SpoofImpl() { |
| if (fSpoofData != NULL) { |
| fSpoofData->removeReference(); // Will delete if refCount goes to zero. |
| } |
| delete fAllowedCharsSet; |
| uprv_free((void *)fAllowedLocales); |
| } |
| |
| // Cast this instance as a USpoofChecker for the C API. |
| USpoofChecker *SpoofImpl::asUSpoofChecker() { |
| return exportForC(); |
| } |
| |
| // |
| // Incoming parameter check on Status and the SpoofChecker object |
| // received from the C API. |
| // |
| const SpoofImpl *SpoofImpl::validateThis(const USpoofChecker *sc, UErrorCode &status) { |
| auto* This = validate(sc, status); |
| if (U_FAILURE(status)) { |
| return NULL; |
| } |
| if (This->fSpoofData != NULL && !This->fSpoofData->validateDataVersion(status)) { |
| return NULL; |
| } |
| return This; |
| } |
| |
| SpoofImpl *SpoofImpl::validateThis(USpoofChecker *sc, UErrorCode &status) { |
| return const_cast<SpoofImpl *> |
| (SpoofImpl::validateThis(const_cast<const USpoofChecker *>(sc), status)); |
| } |
| |
| |
| void SpoofImpl::setAllowedLocales(const char *localesList, UErrorCode &status) { |
| UnicodeSet allowedChars; |
| UnicodeSet *tmpSet = NULL; |
| const char *locStart = localesList; |
| const char *locEnd = NULL; |
| const char *localesListEnd = localesList + uprv_strlen(localesList); |
| int32_t localeListCount = 0; // Number of locales provided by caller. |
| |
| // Loop runs once per locale from the localesList, a comma separated list of locales. |
| do { |
| locEnd = uprv_strchr(locStart, ','); |
| if (locEnd == NULL) { |
| locEnd = localesListEnd; |
| } |
| while (*locStart == ' ') { |
| locStart++; |
| } |
| const char *trimmedEnd = locEnd-1; |
| while (trimmedEnd > locStart && *trimmedEnd == ' ') { |
| trimmedEnd--; |
| } |
| if (trimmedEnd <= locStart) { |
| break; |
| } |
| const char *locale = uprv_strndup(locStart, (int32_t)(trimmedEnd + 1 - locStart)); |
| localeListCount++; |
| |
| // We have one locale from the locales list. |
| // Add the script chars for this locale to the accumulating set of allowed chars. |
| // If the locale is no good, we will be notified back via status. |
| addScriptChars(locale, &allowedChars, status); |
| uprv_free((void *)locale); |
| if (U_FAILURE(status)) { |
| break; |
| } |
| locStart = locEnd + 1; |
| } while (locStart < localesListEnd); |
| |
| // If our caller provided an empty list of locales, we disable the allowed characters checking |
| if (localeListCount == 0) { |
| uprv_free((void *)fAllowedLocales); |
| fAllowedLocales = uprv_strdup(""); |
| tmpSet = new UnicodeSet(0, 0x10ffff); |
| if (fAllowedLocales == NULL || tmpSet == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| tmpSet->freeze(); |
| delete fAllowedCharsSet; |
| fAllowedCharsSet = tmpSet; |
| fChecks &= ~USPOOF_CHAR_LIMIT; |
| return; |
| } |
| |
| |
| // Add all common and inherited characters to the set of allowed chars. |
| UnicodeSet tempSet; |
| tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_COMMON, status); |
| allowedChars.addAll(tempSet); |
| tempSet.applyIntPropertyValue(UCHAR_SCRIPT, USCRIPT_INHERITED, status); |
| allowedChars.addAll(tempSet); |
| |
| // If anything went wrong, we bail out without changing |
| // the state of the spoof checker. |
| if (U_FAILURE(status)) { |
| return; |
| } |
| |
| // Store the updated spoof checker state. |
| tmpSet = allowedChars.clone(); |
| const char *tmpLocalesList = uprv_strdup(localesList); |
| if (tmpSet == NULL || tmpLocalesList == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| uprv_free((void *)fAllowedLocales); |
| fAllowedLocales = tmpLocalesList; |
| tmpSet->freeze(); |
| delete fAllowedCharsSet; |
| fAllowedCharsSet = tmpSet; |
| fChecks |= USPOOF_CHAR_LIMIT; |
| } |
| |
| |
| const char * SpoofImpl::getAllowedLocales(UErrorCode &/*status*/) { |
| return fAllowedLocales; |
| } |
| |
| |
| // Given a locale (a language), add all the characters from all of the scripts used with that language |
| // to the allowedChars UnicodeSet |
| |
| void SpoofImpl::addScriptChars(const char *locale, UnicodeSet *allowedChars, UErrorCode &status) { |
| UScriptCode scripts[30]; |
| |
| int32_t numScripts = uscript_getCode(locale, scripts, UPRV_LENGTHOF(scripts), &status); |
| if (U_FAILURE(status)) { |
| return; |
| } |
| if (status == U_USING_DEFAULT_WARNING) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| UnicodeSet tmpSet; |
| int32_t i; |
| for (i=0; i<numScripts; i++) { |
| tmpSet.applyIntPropertyValue(UCHAR_SCRIPT, scripts[i], status); |
| allowedChars->addAll(tmpSet); |
| } |
| } |
| |
| // Computes the augmented script set for a code point, according to UTS 39 section 5.1. |
| void SpoofImpl::getAugmentedScriptSet(UChar32 codePoint, ScriptSet& result, UErrorCode& status) { |
| result.resetAll(); |
| result.setScriptExtensions(codePoint, status); |
| if (U_FAILURE(status)) { return; } |
| |
| // Section 5.1 step 1 |
| if (result.test(USCRIPT_HAN, status)) { |
| result.set(USCRIPT_HAN_WITH_BOPOMOFO, status); |
| result.set(USCRIPT_JAPANESE, status); |
| result.set(USCRIPT_KOREAN, status); |
| } |
| if (result.test(USCRIPT_HIRAGANA, status)) { |
| result.set(USCRIPT_JAPANESE, status); |
| } |
| if (result.test(USCRIPT_KATAKANA, status)) { |
| result.set(USCRIPT_JAPANESE, status); |
| } |
| if (result.test(USCRIPT_HANGUL, status)) { |
| result.set(USCRIPT_KOREAN, status); |
| } |
| if (result.test(USCRIPT_BOPOMOFO, status)) { |
| result.set(USCRIPT_HAN_WITH_BOPOMOFO, status); |
| } |
| |
| // Section 5.1 step 2 |
| if (result.test(USCRIPT_COMMON, status) || result.test(USCRIPT_INHERITED, status)) { |
| result.setAll(); |
| } |
| } |
| |
| // Computes the resolved script set for a string, according to UTS 39 section 5.1. |
| void SpoofImpl::getResolvedScriptSet(const UnicodeString& input, ScriptSet& result, UErrorCode& status) const { |
| getResolvedScriptSetWithout(input, USCRIPT_CODE_LIMIT, result, status); |
| } |
| |
| // Computes the resolved script set for a string, omitting characters having the specified script. |
| // If USCRIPT_CODE_LIMIT is passed as the second argument, all characters are included. |
| void SpoofImpl::getResolvedScriptSetWithout(const UnicodeString& input, UScriptCode script, ScriptSet& result, UErrorCode& status) const { |
| result.setAll(); |
| |
| ScriptSet temp; |
| UChar32 codePoint; |
| for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) { |
| codePoint = input.char32At(i); |
| |
| // Compute the augmented script set for the character |
| getAugmentedScriptSet(codePoint, temp, status); |
| if (U_FAILURE(status)) { return; } |
| |
| // Intersect the augmented script set with the resolved script set, but only if the character doesn't |
| // have the script specified in the function call |
| if (script == USCRIPT_CODE_LIMIT || !temp.test(script, status)) { |
| result.intersect(temp); |
| } |
| } |
| } |
| |
| // Computes the set of numerics for a string, according to UTS 39 section 5.3. |
| void SpoofImpl::getNumerics(const UnicodeString& input, UnicodeSet& result, UErrorCode& /*status*/) const { |
| result.clear(); |
| |
| UChar32 codePoint; |
| for (int32_t i = 0; i < input.length(); i += U16_LENGTH(codePoint)) { |
| codePoint = input.char32At(i); |
| |
| // Store a representative character for each kind of decimal digit |
| if (u_charType(codePoint) == U_DECIMAL_DIGIT_NUMBER) { |
| // Store the zero character as a representative for comparison. |
| // Unicode guarantees it is codePoint - value |
| result.add(codePoint - (UChar32)u_getNumericValue(codePoint)); |
| } |
| } |
| } |
| |
| // Computes the restriction level of a string, according to UTS 39 section 5.2. |
| URestrictionLevel SpoofImpl::getRestrictionLevel(const UnicodeString& input, UErrorCode& status) const { |
| // Section 5.2 step 1: |
| if (!fAllowedCharsSet->containsAll(input)) { |
| return USPOOF_UNRESTRICTIVE; |
| } |
| |
| // Section 5.2 step 2 |
| // Java use a static UnicodeSet for this test. In C++, avoid the static variable |
| // and just do a simple for loop. |
| UBool allASCII = TRUE; |
| for (int32_t i=0, length=input.length(); i<length; i++) { |
| if (input.charAt(i) > 0x7f) { |
| allASCII = FALSE; |
| break; |
| } |
| } |
| if (allASCII) { |
| return USPOOF_ASCII; |
| } |
| |
| // Section 5.2 steps 3: |
| ScriptSet resolvedScriptSet; |
| getResolvedScriptSet(input, resolvedScriptSet, status); |
| if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; } |
| |
| // Section 5.2 step 4: |
| if (!resolvedScriptSet.isEmpty()) { |
| return USPOOF_SINGLE_SCRIPT_RESTRICTIVE; |
| } |
| |
| // Section 5.2 step 5: |
| ScriptSet resolvedNoLatn; |
| getResolvedScriptSetWithout(input, USCRIPT_LATIN, resolvedNoLatn, status); |
| if (U_FAILURE(status)) { return USPOOF_UNRESTRICTIVE; } |
| |
| // Section 5.2 step 6: |
| if (resolvedNoLatn.test(USCRIPT_HAN_WITH_BOPOMOFO, status) |
| || resolvedNoLatn.test(USCRIPT_JAPANESE, status) |
| || resolvedNoLatn.test(USCRIPT_KOREAN, status)) { |
| return USPOOF_HIGHLY_RESTRICTIVE; |
| } |
| |
| // Section 5.2 step 7: |
| if (!resolvedNoLatn.isEmpty() |
| && !resolvedNoLatn.test(USCRIPT_CYRILLIC, status) |
| && !resolvedNoLatn.test(USCRIPT_GREEK, status) |
| && !resolvedNoLatn.test(USCRIPT_CHEROKEE, status)) { |
| return USPOOF_MODERATELY_RESTRICTIVE; |
| } |
| |
| // Section 5.2 step 8: |
| return USPOOF_MINIMALLY_RESTRICTIVE; |
| } |
| |
| int32_t SpoofImpl::findHiddenOverlay(const UnicodeString& input, UErrorCode&) const { |
| bool sawLeadCharacter = false; |
| for (int32_t i=0; i<input.length();) { |
| UChar32 cp = input.char32At(i); |
| if (sawLeadCharacter && cp == 0x0307) { |
| return i; |
| } |
| uint8_t combiningClass = u_getCombiningClass(cp); |
| // Skip over characters except for those with combining class 0 (non-combining characters) or with |
| // combining class 230 (same class as U+0307) |
| U_ASSERT(u_getCombiningClass(0x0307) == 230); |
| if (combiningClass == 0 || combiningClass == 230) { |
| sawLeadCharacter = isIllegalCombiningDotLeadCharacter(cp); |
| } |
| i += U16_LENGTH(cp); |
| } |
| return -1; |
| } |
| |
| static inline bool isIllegalCombiningDotLeadCharacterNoLookup(UChar32 cp) { |
| return cp == u'i' || cp == u'j' || cp == u'ı' || cp == u'ȷ' || cp == u'l' || |
| u_hasBinaryProperty(cp, UCHAR_SOFT_DOTTED); |
| } |
| |
| bool SpoofImpl::isIllegalCombiningDotLeadCharacter(UChar32 cp) const { |
| if (isIllegalCombiningDotLeadCharacterNoLookup(cp)) { |
| return true; |
| } |
| UnicodeString skelStr; |
| fSpoofData->confusableLookup(cp, skelStr); |
| UChar32 finalCp = skelStr.char32At(skelStr.moveIndex32(skelStr.length(), -1)); |
| if (finalCp != cp && isIllegalCombiningDotLeadCharacterNoLookup(finalCp)) { |
| return true; |
| } |
| return false; |
| } |
| |
| |
| |
| // Convert a text format hex number. Utility function used by builder code. Static. |
| // Input: UChar *string text. Output: a UChar32 |
| // Input has been pre-checked, and will have no non-hex chars. |
| // The number must fall in the code point range of 0..0x10ffff |
| // Static Function. |
| UChar32 SpoofImpl::ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return 0; |
| } |
| U_ASSERT(limit-start > 0); |
| uint32_t val = 0; |
| int i; |
| for (i=start; i<limit; i++) { |
| int digitVal = s[i] - 0x30; |
| if (digitVal>9) { |
| digitVal = 0xa + (s[i] - 0x41); // Upper Case 'A' |
| } |
| if (digitVal>15) { |
| digitVal = 0xa + (s[i] - 0x61); // Lower Case 'a' |
| } |
| U_ASSERT(digitVal <= 0xf); |
| val <<= 4; |
| val += digitVal; |
| } |
| if (val > 0x10ffff) { |
| status = U_PARSE_ERROR; |
| val = 0; |
| } |
| return (UChar32)val; |
| } |
| |
| |
| //----------------------------------------- |
| // |
| // class CheckResult Implementation |
| // |
| //----------------------------------------- |
| |
| CheckResult::CheckResult() { |
| clear(); |
| } |
| |
| USpoofCheckResult* CheckResult::asUSpoofCheckResult() { |
| return exportForC(); |
| } |
| |
| // |
| // Incoming parameter check on Status and the CheckResult object |
| // received from the C API. |
| // |
| const CheckResult* CheckResult::validateThis(const USpoofCheckResult *ptr, UErrorCode &status) { |
| return validate(ptr, status); |
| } |
| |
| CheckResult* CheckResult::validateThis(USpoofCheckResult *ptr, UErrorCode &status) { |
| return validate(ptr, status); |
| } |
| |
| void CheckResult::clear() { |
| fChecks = 0; |
| fNumerics.clear(); |
| fRestrictionLevel = USPOOF_UNDEFINED_RESTRICTIVE; |
| } |
| |
| int32_t CheckResult::toCombinedBitmask(int32_t enabledChecks) { |
| if ((enabledChecks & USPOOF_AUX_INFO) != 0 && fRestrictionLevel != USPOOF_UNDEFINED_RESTRICTIVE) { |
| return fChecks | fRestrictionLevel; |
| } else { |
| return fChecks; |
| } |
| } |
| |
| CheckResult::~CheckResult() { |
| } |
| |
| //---------------------------------------------------------------------------------------------- |
| // |
| // class SpoofData Implementation |
| // |
| //---------------------------------------------------------------------------------------------- |
| |
| |
| UBool SpoofData::validateDataVersion(UErrorCode &status) const { |
| if (U_FAILURE(status) || |
| fRawData == NULL || |
| fRawData->fMagic != USPOOF_MAGIC || |
| fRawData->fFormatVersion[0] != USPOOF_CONFUSABLE_DATA_FORMAT_VERSION || |
| fRawData->fFormatVersion[1] != 0 || |
| fRawData->fFormatVersion[2] != 0 || |
| fRawData->fFormatVersion[3] != 0) { |
| status = U_INVALID_FORMAT_ERROR; |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static UBool U_CALLCONV |
| spoofDataIsAcceptable(void *context, |
| const char * /* type */, const char * /*name*/, |
| const UDataInfo *pInfo) { |
| if( |
| pInfo->size >= 20 && |
| pInfo->isBigEndian == U_IS_BIG_ENDIAN && |
| pInfo->charsetFamily == U_CHARSET_FAMILY && |
| pInfo->dataFormat[0] == 0x43 && // dataFormat="Cfu " |
| pInfo->dataFormat[1] == 0x66 && |
| pInfo->dataFormat[2] == 0x75 && |
| pInfo->dataFormat[3] == 0x20 && |
| pInfo->formatVersion[0] == USPOOF_CONFUSABLE_DATA_FORMAT_VERSION |
| ) { |
| UVersionInfo *version = static_cast<UVersionInfo *>(context); |
| if(version != NULL) { |
| uprv_memcpy(version, pInfo->dataVersion, 4); |
| } |
| return TRUE; |
| } else { |
| return FALSE; |
| } |
| } |
| |
| // Methods for the loading of the default confusables data file. The confusable |
| // data is loaded only when it is needed. |
| // |
| // SpoofData::getDefault() - Return the default confusables data, and call the |
| // initOnce() if it is not available. Adds a reference |
| // to the SpoofData that the caller is responsible for |
| // decrementing when they are done with the data. |
| // |
| // uspoof_loadDefaultData - Called once, from initOnce(). The resulting SpoofData |
| // is shared by all spoof checkers using the default data. |
| // |
| // uspoof_cleanupDefaultData - Called during cleanup. |
| // |
| |
| static UInitOnce gSpoofInitDefaultOnce = U_INITONCE_INITIALIZER; |
| static SpoofData* gDefaultSpoofData; |
| |
| static UBool U_CALLCONV |
| uspoof_cleanupDefaultData(void) { |
| if (gDefaultSpoofData) { |
| // Will delete, assuming all user-level spoof checkers were closed. |
| gDefaultSpoofData->removeReference(); |
| gDefaultSpoofData = nullptr; |
| gSpoofInitDefaultOnce.reset(); |
| } |
| return TRUE; |
| } |
| |
| static void U_CALLCONV uspoof_loadDefaultData(UErrorCode& status) { |
| UDataMemory *udm = udata_openChoice(nullptr, "cfu", "confusables", |
| spoofDataIsAcceptable, |
| nullptr, // context, would receive dataVersion if supplied. |
| &status); |
| if (U_FAILURE(status)) { return; } |
| gDefaultSpoofData = new SpoofData(udm, status); |
| if (U_FAILURE(status)) { |
| delete gDefaultSpoofData; |
| gDefaultSpoofData = nullptr; |
| return; |
| } |
| if (gDefaultSpoofData == nullptr) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| ucln_i18n_registerCleanup(UCLN_I18N_SPOOFDATA, uspoof_cleanupDefaultData); |
| } |
| |
| SpoofData* SpoofData::getDefault(UErrorCode& status) { |
| umtx_initOnce(gSpoofInitDefaultOnce, &uspoof_loadDefaultData, status); |
| if (U_FAILURE(status)) { return NULL; } |
| gDefaultSpoofData->addReference(); |
| return gDefaultSpoofData; |
| } |
| |
| |
| |
| SpoofData::SpoofData(UDataMemory *udm, UErrorCode &status) |
| { |
| reset(); |
| if (U_FAILURE(status)) { |
| return; |
| } |
| fUDM = udm; |
| // fRawData is non-const because it may be constructed by the data builder. |
| fRawData = reinterpret_cast<SpoofDataHeader *>( |
| const_cast<void *>(udata_getMemory(udm))); |
| validateDataVersion(status); |
| initPtrs(status); |
| } |
| |
| |
| SpoofData::SpoofData(const void *data, int32_t length, UErrorCode &status) |
| { |
| reset(); |
| if (U_FAILURE(status)) { |
| return; |
| } |
| if ((size_t)length < sizeof(SpoofDataHeader)) { |
| status = U_INVALID_FORMAT_ERROR; |
| return; |
| } |
| if (data == NULL) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| void *ncData = const_cast<void *>(data); |
| fRawData = static_cast<SpoofDataHeader *>(ncData); |
| if (length < fRawData->fLength) { |
| status = U_INVALID_FORMAT_ERROR; |
| return; |
| } |
| validateDataVersion(status); |
| initPtrs(status); |
| } |
| |
| |
| // Spoof Data constructor for use from data builder. |
| // Initializes a new, empty data area that will be populated later. |
| SpoofData::SpoofData(UErrorCode &status) { |
| reset(); |
| if (U_FAILURE(status)) { |
| return; |
| } |
| fDataOwned = true; |
| |
| // The spoof header should already be sized to be a multiple of 16 bytes. |
| // Just in case it's not, round it up. |
| uint32_t initialSize = (sizeof(SpoofDataHeader) + 15) & ~15; |
| U_ASSERT(initialSize == sizeof(SpoofDataHeader)); |
| |
| fRawData = static_cast<SpoofDataHeader *>(uprv_malloc(initialSize)); |
| fMemLimit = initialSize; |
| if (fRawData == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return; |
| } |
| uprv_memset(fRawData, 0, initialSize); |
| |
| fRawData->fMagic = USPOOF_MAGIC; |
| fRawData->fFormatVersion[0] = USPOOF_CONFUSABLE_DATA_FORMAT_VERSION; |
| fRawData->fFormatVersion[1] = 0; |
| fRawData->fFormatVersion[2] = 0; |
| fRawData->fFormatVersion[3] = 0; |
| initPtrs(status); |
| } |
| |
| // reset() - initialize all fields. |
| // Should be updated if any new fields are added. |
| // Called by constructors to put things in a known initial state. |
| void SpoofData::reset() { |
| fRawData = NULL; |
| fDataOwned = FALSE; |
| fUDM = NULL; |
| fMemLimit = 0; |
| fRefCount = 1; |
| fCFUKeys = NULL; |
| fCFUValues = NULL; |
| fCFUStrings = NULL; |
| } |
| |
| |
| // SpoofData::initPtrs() |
| // Initialize the pointers to the various sections of the raw data. |
| // |
| // This function is used both during the Trie building process (multiple |
| // times, as the individual data sections are added), and |
| // during the opening of a Spoof Checker from prebuilt data. |
| // |
| // The pointers for non-existent data sections (identified by an offset of 0) |
| // are set to NULL. |
| // |
| // Note: During building the data, adding each new data section |
| // reallocs the raw data area, which likely relocates it, which |
| // in turn requires reinitializing all of the pointers into it, hence |
| // multiple calls to this function during building. |
| // |
| void SpoofData::initPtrs(UErrorCode &status) { |
| fCFUKeys = NULL; |
| fCFUValues = NULL; |
| fCFUStrings = NULL; |
| if (U_FAILURE(status)) { |
| return; |
| } |
| if (fRawData->fCFUKeys != 0) { |
| fCFUKeys = (int32_t *)((char *)fRawData + fRawData->fCFUKeys); |
| } |
| if (fRawData->fCFUStringIndex != 0) { |
| fCFUValues = (uint16_t *)((char *)fRawData + fRawData->fCFUStringIndex); |
| } |
| if (fRawData->fCFUStringTable != 0) { |
| fCFUStrings = (UChar *)((char *)fRawData + fRawData->fCFUStringTable); |
| } |
| } |
| |
| |
| SpoofData::~SpoofData() { |
| if (fDataOwned) { |
| uprv_free(fRawData); |
| } |
| fRawData = NULL; |
| if (fUDM != NULL) { |
| udata_close(fUDM); |
| } |
| fUDM = NULL; |
| } |
| |
| |
| void SpoofData::removeReference() { |
| if (umtx_atomic_dec(&fRefCount) == 0) { |
| delete this; |
| } |
| } |
| |
| |
| SpoofData *SpoofData::addReference() { |
| umtx_atomic_inc(&fRefCount); |
| return this; |
| } |
| |
| |
| void *SpoofData::reserveSpace(int32_t numBytes, UErrorCode &status) { |
| if (U_FAILURE(status)) { |
| return NULL; |
| } |
| if (!fDataOwned) { |
| UPRV_UNREACHABLE; |
| } |
| |
| numBytes = (numBytes + 15) & ~15; // Round up to a multiple of 16 |
| uint32_t returnOffset = fMemLimit; |
| fMemLimit += numBytes; |
| fRawData = static_cast<SpoofDataHeader *>(uprv_realloc(fRawData, fMemLimit)); |
| fRawData->fLength = fMemLimit; |
| uprv_memset((char *)fRawData + returnOffset, 0, numBytes); |
| initPtrs(status); |
| return (char *)fRawData + returnOffset; |
| } |
| |
| int32_t SpoofData::serialize(void *buf, int32_t capacity, UErrorCode &status) const { |
| int32_t dataSize = fRawData->fLength; |
| if (capacity < dataSize) { |
| status = U_BUFFER_OVERFLOW_ERROR; |
| return dataSize; |
| } |
| uprv_memcpy(buf, fRawData, dataSize); |
| return dataSize; |
| } |
| |
| int32_t SpoofData::size() const { |
| return fRawData->fLength; |
| } |
| |
| //------------------------------- |
| // |
| // Front-end APIs for SpoofData |
| // |
| //------------------------------- |
| |
| int32_t SpoofData::confusableLookup(UChar32 inChar, UnicodeString &dest) const { |
| // Perform a binary search. |
| // [lo, hi), i.e lo is inclusive, hi is exclusive. |
| // The result after the loop will be in lo. |
| int32_t lo = 0; |
| int32_t hi = length(); |
| do { |
| int32_t mid = (lo + hi) / 2; |
| if (codePointAt(mid) > inChar) { |
| hi = mid; |
| } else if (codePointAt(mid) < inChar) { |
| lo = mid; |
| } else { |
| // Found result. Break early. |
| lo = mid; |
| break; |
| } |
| } while (hi - lo > 1); |
| |
| // Did we find an entry? If not, the char maps to itself. |
| if (codePointAt(lo) != inChar) { |
| dest.append(inChar); |
| return 1; |
| } |
| |
| // Add the element to the string builder and return. |
| return appendValueTo(lo, dest); |
| } |
| |
| int32_t SpoofData::length() const { |
| return fRawData->fCFUKeysSize; |
| } |
| |
| UChar32 SpoofData::codePointAt(int32_t index) const { |
| return ConfusableDataUtils::keyToCodePoint(fCFUKeys[index]); |
| } |
| |
| int32_t SpoofData::appendValueTo(int32_t index, UnicodeString& dest) const { |
| int32_t stringLength = ConfusableDataUtils::keyToLength(fCFUKeys[index]); |
| |
| // Value is either a char (for strings of length 1) or |
| // an index into the string table (for longer strings) |
| uint16_t value = fCFUValues[index]; |
| if (stringLength == 1) { |
| dest.append((UChar)value); |
| } else { |
| dest.append(fCFUStrings + value, stringLength); |
| } |
| |
| return stringLength; |
| } |
| |
| |
| U_NAMESPACE_END |
| |
| U_NAMESPACE_USE |
| |
| //----------------------------------------------------------------------------- |
| // |
| // uspoof_swap - byte swap and char encoding swap of spoof data |
| // |
| //----------------------------------------------------------------------------- |
| U_CAPI int32_t U_EXPORT2 |
| uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, |
| UErrorCode *status) { |
| |
| if (status == NULL || U_FAILURE(*status)) { |
| return 0; |
| } |
| if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) { |
| *status=U_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| |
| // |
| // Check that the data header is for spoof data. |
| // (Header contents are defined in gencfu.cpp) |
| // |
| const UDataInfo *pInfo = (const UDataInfo *)((const char *)inData+4); |
| if(!( pInfo->dataFormat[0]==0x43 && /* dataFormat="Cfu " */ |
| pInfo->dataFormat[1]==0x66 && |
| pInfo->dataFormat[2]==0x75 && |
| pInfo->dataFormat[3]==0x20 && |
| pInfo->formatVersion[0]==USPOOF_CONFUSABLE_DATA_FORMAT_VERSION && |
| pInfo->formatVersion[1]==0 && |
| pInfo->formatVersion[2]==0 && |
| pInfo->formatVersion[3]==0 )) { |
| udata_printError(ds, "uspoof_swap(): data format %02x.%02x.%02x.%02x " |
| "(format version %02x %02x %02x %02x) is not recognized\n", |
| pInfo->dataFormat[0], pInfo->dataFormat[1], |
| pInfo->dataFormat[2], pInfo->dataFormat[3], |
| pInfo->formatVersion[0], pInfo->formatVersion[1], |
| pInfo->formatVersion[2], pInfo->formatVersion[3]); |
| *status=U_UNSUPPORTED_ERROR; |
| return 0; |
| } |
| |
| // |
| // Swap the data header. (This is the generic ICU Data Header, not the uspoof Specific |
| // header). This swap also conveniently gets us |
| // the size of the ICU d.h., which lets us locate the start |
| // of the uspoof specific data. |
| // |
| int32_t headerSize=udata_swapDataHeader(ds, inData, length, outData, status); |
| |
| |
| // |
| // Get the Spoof Data Header, and check that it appears to be OK. |
| // |
| // |
| const uint8_t *inBytes =(const uint8_t *)inData+headerSize; |
| SpoofDataHeader *spoofDH = (SpoofDataHeader *)inBytes; |
| if (ds->readUInt32(spoofDH->fMagic) != USPOOF_MAGIC || |
| ds->readUInt32(spoofDH->fLength) < sizeof(SpoofDataHeader)) |
| { |
| udata_printError(ds, "uspoof_swap(): Spoof Data header is invalid.\n"); |
| *status=U_UNSUPPORTED_ERROR; |
| return 0; |
| } |
| |
| // |
| // Prefight operation? Just return the size |
| // |
| int32_t spoofDataLength = ds->readUInt32(spoofDH->fLength); |
| int32_t totalSize = headerSize + spoofDataLength; |
| if (length < 0) { |
| return totalSize; |
| } |
| |
| // |
| // Check that length passed in is consistent with length from Spoof data header. |
| // |
| if (length < totalSize) { |
| udata_printError(ds, "uspoof_swap(): too few bytes (%d after ICU Data header) for spoof data.\n", |
| spoofDataLength); |
| *status=U_INDEX_OUTOFBOUNDS_ERROR; |
| return 0; |
| } |
| |
| |
| // |
| // Swap the Data. Do the data itself first, then the Spoof Data Header, because |
| // we need to reference the header to locate the data, and an |
| // inplace swap of the header leaves it unusable. |
| // |
| uint8_t *outBytes = (uint8_t *)outData + headerSize; |
| SpoofDataHeader *outputDH = (SpoofDataHeader *)outBytes; |
| |
| int32_t sectionStart; |
| int32_t sectionLength; |
| |
| // |
| // If not swapping in place, zero out the output buffer before starting. |
| // Gaps may exist between the individual sections, and these must be zeroed in |
| // the output buffer. The simplest way to do that is to just zero the whole thing. |
| // |
| if (inBytes != outBytes) { |
| uprv_memset(outBytes, 0, spoofDataLength); |
| } |
| |
| // Confusables Keys Section (fCFUKeys) |
| sectionStart = ds->readUInt32(spoofDH->fCFUKeys); |
| sectionLength = ds->readUInt32(spoofDH->fCFUKeysSize) * 4; |
| ds->swapArray32(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); |
| |
| // String Index Section |
| sectionStart = ds->readUInt32(spoofDH->fCFUStringIndex); |
| sectionLength = ds->readUInt32(spoofDH->fCFUStringIndexSize) * 2; |
| ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); |
| |
| // String Table Section |
| sectionStart = ds->readUInt32(spoofDH->fCFUStringTable); |
| sectionLength = ds->readUInt32(spoofDH->fCFUStringTableLen) * 2; |
| ds->swapArray16(ds, inBytes+sectionStart, sectionLength, outBytes+sectionStart, status); |
| |
| // And, last, swap the header itself. |
| // int32_t fMagic // swap this |
| // uint8_t fFormatVersion[4] // Do not swap this, just copy |
| // int32_t fLength and all the rest // Swap the rest, all is 32 bit stuff. |
| // |
| uint32_t magic = ds->readUInt32(spoofDH->fMagic); |
| ds->writeUInt32((uint32_t *)&outputDH->fMagic, magic); |
| |
| if (outputDH->fFormatVersion != spoofDH->fFormatVersion) { |
| uprv_memcpy(outputDH->fFormatVersion, spoofDH->fFormatVersion, sizeof(spoofDH->fFormatVersion)); |
| } |
| // swap starting at fLength |
| ds->swapArray32(ds, &spoofDH->fLength, sizeof(SpoofDataHeader)-8 /* minus magic and fFormatVersion[4] */, &outputDH->fLength, status); |
| |
| return totalSize; |
| } |
| |
| #endif |
| |
| |