From 4373a0bb865265f07507d36e6c151a556f3d94e8 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 12 Nov 2021 10:12:37 +0200 Subject: [PATCH] [TextServer] Add ICU Unicode security and spoofing detection. --- doc/classes/TextServer.xml | 21 + doc/classes/TextServerExtension.xml | 15 + modules/text_server_adv/SCsub | 11 +- modules/text_server_adv/text_server_adv.cpp | 63 + modules/text_server_adv/text_server_adv.h | 4 + servers/text/text_server_extension.cpp | 19 + servers/text/text_server_extension.h | 5 + servers/text_server.cpp | 4 + servers/text_server.h | 4 + thirdparty/README.md | 2 + thirdparty/icu4c/godot_data.json | 3 +- thirdparty/icu4c/i18n/scriptset.cpp | 313 ++++ thirdparty/icu4c/i18n/scriptset.h | 86 + thirdparty/icu4c/i18n/ucln_in.cpp | 65 + thirdparty/icu4c/i18n/ucln_in.h | 74 + thirdparty/icu4c/i18n/unicode/uspoof.h | 1577 +++++++++++++++++++ thirdparty/icu4c/i18n/uspoof.cpp | 839 ++++++++++ thirdparty/icu4c/i18n/uspoof_impl.cpp | 959 +++++++++++ thirdparty/icu4c/i18n/uspoof_impl.h | 343 ++++ thirdparty/icu4c/icudt71l.dat | Bin 4226000 -> 4271680 bytes 20 files changed, 4403 insertions(+), 4 deletions(-) create mode 100644 thirdparty/icu4c/i18n/scriptset.cpp create mode 100644 thirdparty/icu4c/i18n/scriptset.h create mode 100644 thirdparty/icu4c/i18n/ucln_in.cpp create mode 100644 thirdparty/icu4c/i18n/ucln_in.h create mode 100644 thirdparty/icu4c/i18n/unicode/uspoof.h create mode 100644 thirdparty/icu4c/i18n/uspoof.cpp create mode 100644 thirdparty/icu4c/i18n/uspoof_impl.cpp create mode 100644 thirdparty/icu4c/i18n/uspoof_impl.h diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index f4c9ade7d1..9f35ddc353 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -937,6 +937,16 @@ Returns [code]true[/code] if the server supports a feature. + + + + + + Returns index of the first string in [code]dict[/dict] which is visually confusable with the [code]string[/string], or [code]-1[/code] if none is found. + [b]Note:[/b] This method doesn't detect invisible characters, for spoof detection use it in combination with [method spoof_check]. + [b]Note:[/b] Always returns [code]-1[/code] if the server does not support the [constant FEATURE_UNICODE_SECURITY] feature. + + @@ -1476,6 +1486,14 @@ Aligns shaped text to the given tab-stops. + + + + + Returns [code]true[/code] if [code]string[/code] is likely to be an attempt at confusing the reader. + [b]Note:[/b] Always returns [code]false[/code] if the server does not support the [constant FEATURE_UNICODE_SECURITY] feature. + + @@ -1733,6 +1751,9 @@ TextServer supports UAX #31 identifier validation, see [method is_valid_identifier]. + + TextServer supports [url=https://unicode.org/reports/tr36/]Unicode Technical Report #36[/url] and [url=https://unicode.org/reports/tr39/]Unicode Technical Standard #39[/url] based spoof detection features. + Contour point is on the curve. diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 482460cb3b..c686a06e5e 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -934,6 +934,14 @@ Returns [code]true[/code] if the server supports a feature. + + + + + + Returns index of the first string in [code]dict[/dict] which is visually confusable with the [code]string[/string], or [code]-1[/code] if none is found. + + @@ -1488,6 +1496,13 @@ [b]Note:[/b] This method is used by default line/word breaking methods, and its implementation might be omitted if custom line breaking in implemented. + + + + + Returns [code]true[/code] if [code]string[/code] is likely to be an attempt at confusing the reader. + + diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index d212fe62b4..73e5c2bf74 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -121,7 +121,7 @@ if env["builtin_harfbuzz"]: env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"]) if env["builtin_icu"]: - env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) + env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"]) if freetype_enabled: @@ -439,6 +439,10 @@ if env["builtin_icu"]: "common/uvectr32.cpp", "common/uvectr64.cpp", "common/wintz.cpp", + "i18n/scriptset.cpp", + "i18n/ucln_in.cpp", + "i18n/uspoof.cpp", + "i18n/uspoof_impl.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] @@ -451,7 +455,7 @@ if env["builtin_icu"]: else: thirdparty_sources += ["icu_data/icudata_stub.cpp"] - env_icu.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) + env_icu.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) env_icu.Append( CXXFLAGS=[ "-DU_STATIC_IMPLEMENTATION", @@ -463,6 +467,7 @@ if env["builtin_icu"]: "-DUCONFIG_NO_IDNA", "-DUCONFIG_NO_FILE_IO", "-DUCONFIG_NO_TRANSLITERATION", + "-DUCONFIG_NO_REGULAR_EXPRESSIONS", "-DPKGDATA_MODE=static", "-DU_ENABLE_DYLOAD=0", "-DU_HAVE_LIB_SUFFIX=1", @@ -480,7 +485,7 @@ if env["builtin_icu"]: if env_text_server_adv["tools"]: env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"]) - env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/"]) + env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) lib = env_icu.add_library("icu_builtin", thirdparty_sources) thirdparty_obj += lib diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index f8dbbc2e61..bb49fb5248 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -347,6 +347,7 @@ bool TextServerAdvanced::has_feature(Feature p_feature) const { case FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION: case FEATURE_USE_SUPPORT_DATA: case FEATURE_UNICODE_IDENTIFIERS: + case FEATURE_UNICODE_SECURITY: return true; default: { } @@ -5640,6 +5641,68 @@ String TextServerAdvanced::percent_sign(const String &p_language) const { return "%"; } +int TextServerAdvanced::is_confusable(const String &p_string, const PackedStringArray &p_dict) const { + UErrorCode status = U_ZERO_ERROR; + int match_index = -1; + + Char16String utf16 = p_string.utf16(); + Vector skeletons; + skeletons.resize(p_dict.size()); + + USpoofChecker *sc = uspoof_open(&status); + uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status); + for (int i = 0; i < p_dict.size(); i++) { + Char16String word = p_dict[i].utf16(); + int32_t len = uspoof_getSkeleton(sc, 0, word.get_data(), -1, NULL, 0, &status); + skeletons.write[i] = (UChar *)memalloc(++len * sizeof(UChar)); + status = U_ZERO_ERROR; + uspoof_getSkeleton(sc, 0, word.get_data(), -1, skeletons.write[i], len, &status); + } + + int32_t len = uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, NULL, 0, &status); + UChar *skel = (UChar *)memalloc(++len * sizeof(UChar)); + status = U_ZERO_ERROR; + uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, skel, len, &status); + for (int i = 0; i < skeletons.size(); i++) { + if (u_strcmp(skel, skeletons[i]) == 0) { + match_index = i; + break; + } + } + memfree(skel); + + for (int i = 0; i < skeletons.size(); i++) { + memfree(skeletons.write[i]); + } + uspoof_close(sc); + + ERR_FAIL_COND_V_MSG(U_FAILURE(status), -1, u_errorName(status)); + + return match_index; +} + +bool TextServerAdvanced::spoof_check(const String &p_string) const { + UErrorCode status = U_ZERO_ERROR; + Char16String utf16 = p_string.utf16(); + + USet *allowed = uset_openEmpty(); + uset_addAll(allowed, uspoof_getRecommendedSet(&status)); + uset_addAll(allowed, uspoof_getInclusionSet(&status)); + + USpoofChecker *sc = uspoof_open(&status); + uspoof_setAllowedChars(sc, allowed, &status); + uspoof_setRestrictionLevel(sc, USPOOF_MODERATELY_RESTRICTIVE); + + int32_t bitmask = uspoof_check(sc, utf16.get_data(), -1, NULL, &status); + + uspoof_close(sc); + uset_close(allowed); + + ERR_FAIL_COND_V_MSG(U_FAILURE(status), false, u_errorName(status)); + + return (bitmask != 0); +} + String TextServerAdvanced::strip_diacritics(const String &p_string) const { UErrorCode err = U_ZERO_ERROR; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 170d92f8fc..b337abea7a 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -101,6 +101,7 @@ using namespace godot; #include #include #include +#include #include #include @@ -701,6 +702,9 @@ public: virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const override; + virtual int is_confusable(const String &p_string, const PackedStringArray &p_dict) const override; + virtual bool spoof_check(const String &p_string) const override; + virtual String strip_diacritics(const String &p_string) const override; virtual bool is_valid_identifier(const String &p_string) const override; diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index c2387be80d..59310ab69b 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -301,6 +301,9 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(string_get_word_breaks, "string", "language"); + GDVIRTUAL_BIND(is_confusable, "string", "dict"); + GDVIRTUAL_BIND(spoof_check, "string"); + GDVIRTUAL_BIND(string_to_upper, "string", "language"); GDVIRTUAL_BIND(string_to_lower, "string", "language"); @@ -1547,6 +1550,22 @@ PackedInt32Array TextServerExtension::string_get_word_breaks(const String &p_str return PackedInt32Array(); } +int TextServerExtension::is_confusable(const String &p_string, const PackedStringArray &p_dict) const { + int ret; + if (GDVIRTUAL_CALL(is_confusable, p_string, p_dict, ret)) { + return ret; + } + return TextServer::is_confusable(p_string, p_dict); +} + +bool TextServerExtension::spoof_check(const String &p_string) const { + bool ret; + if (GDVIRTUAL_CALL(spoof_check, p_string, ret)) { + return ret; + } + return TextServer::spoof_check(p_string); +} + TextServerExtension::TextServerExtension() { //NOP } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 3814e2ad88..81af1b60e5 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -507,6 +507,11 @@ public: Array parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; GDVIRTUAL3RC(Array, parse_structured_text, StructuredTextParser, const Array &, const String &); + virtual int is_confusable(const String &p_string, const PackedStringArray &p_dict) const override; + virtual bool spoof_check(const String &p_string) const override; + GDVIRTUAL2RC(int, is_confusable, const String &, const PackedStringArray &); + GDVIRTUAL1RC(bool, spoof_check, const String &); + TextServerExtension(); ~TextServerExtension(); }; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 1ea05795f1..fd63a7b99d 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -446,6 +446,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language"), &TextServer::string_get_word_breaks, DEFVAL("")); + ClassDB::bind_method(D_METHOD("is_confusable", "string", "dict"), &TextServer::is_confusable); + ClassDB::bind_method(D_METHOD("spoof_check", "string"), &TextServer::spoof_check); + ClassDB::bind_method(D_METHOD("strip_diacritics", "string"), &TextServer::strip_diacritics); ClassDB::bind_method(D_METHOD("is_valid_identifier", "string"), &TextServer::is_valid_identifier); @@ -547,6 +550,7 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION); BIND_ENUM_CONSTANT(FEATURE_USE_SUPPORT_DATA); BIND_ENUM_CONSTANT(FEATURE_UNICODE_IDENTIFIERS); + BIND_ENUM_CONSTANT(FEATURE_UNICODE_SECURITY); /* FT Contour Point Types */ BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_ON); diff --git a/servers/text_server.h b/servers/text_server.h index 0e57bcbb6c..5874b8f6e8 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -149,6 +149,7 @@ public: FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION = 1 << 11, FEATURE_USE_SUPPORT_DATA = 1 << 12, FEATURE_UNICODE_IDENTIFIERS = 1 << 13, + FEATURE_UNICODE_SECURITY = 1 << 14, }; enum ContourPointTag { @@ -464,6 +465,9 @@ public: // String functions. virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const = 0; + virtual int is_confusable(const String &p_string, const PackedStringArray &p_dict) const { return -1; }; + virtual bool spoof_check(const String &p_string) const { return false; }; + virtual String strip_diacritics(const String &p_string) const; virtual bool is_valid_identifier(const String &p_string) const; diff --git a/thirdparty/README.md b/thirdparty/README.md index b06d9cec81..6db011d3c6 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -231,6 +231,8 @@ Files extracted from upstream source: Files extracted from upstream source: - the `common` folder +- `scriptset.*`, `ucln_in.*`, `uspoof.cpp"` and `uspoof_impl.cpp` from the `i18n` folder +- `uspoof.h` from the `i18n/unicode` folder - `LICENSE` Files generated from upstream source: diff --git a/thirdparty/icu4c/godot_data.json b/thirdparty/icu4c/godot_data.json index 3a9c28af4c..e36e2b078b 100644 --- a/thirdparty/icu4c/godot_data.json +++ b/thirdparty/icu4c/godot_data.json @@ -6,5 +6,6 @@ brkitr_tree: include misc: include normalization: include + confusables: include } -} \ No newline at end of file +} diff --git a/thirdparty/icu4c/i18n/scriptset.cpp b/thirdparty/icu4c/i18n/scriptset.cpp new file mode 100644 index 0000000000..6a1db8c01c --- /dev/null +++ b/thirdparty/icu4c/i18n/scriptset.cpp @@ -0,0 +1,313 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* scriptset.cpp +* +* created on: 2013 Jan 7 +* created by: Andy Heninger +*/ + +#include "unicode/utypes.h" + +#include "unicode/uchar.h" +#include "unicode/unistr.h" + +#include "scriptset.h" +#include "uassert.h" +#include "cmemory.h" + +U_NAMESPACE_BEGIN + +//---------------------------------------------------------------------------- +// +// ScriptSet implementation +// +//---------------------------------------------------------------------------- +ScriptSet::ScriptSet() { + uprv_memset(bits, 0, sizeof(bits)); +} + +ScriptSet::~ScriptSet() { +} + +ScriptSet::ScriptSet(const ScriptSet &other) { + *this = other; +} + +ScriptSet & ScriptSet::operator =(const ScriptSet &other) { + uprv_memcpy(bits, other.bits, sizeof(bits)); + return *this; +} + +bool ScriptSet::operator == (const ScriptSet &other) const { + for (uint32_t i=0; i= SCRIPT_LIMIT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return FALSE; + } + uint32_t index = script / 32; + uint32_t bit = 1 << (script & 31); + return ((bits[index] & bit) != 0); +} + + +ScriptSet &ScriptSet::set(UScriptCode script, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (script < 0 || (int32_t)script >= SCRIPT_LIMIT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + uint32_t index = script / 32; + uint32_t bit = 1 << (script & 31); + bits[index] |= bit; + return *this; +} + +ScriptSet &ScriptSet::reset(UScriptCode script, UErrorCode &status) { + if (U_FAILURE(status)) { + return *this; + } + if (script < 0 || (int32_t)script >= SCRIPT_LIMIT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return *this; + } + uint32_t index = script / 32; + uint32_t bit = 1 << (script & 31); + bits[index] &= ~bit; + return *this; +} + + + +ScriptSet &ScriptSet::Union(const ScriptSet &other) { + for (uint32_t i=0; iintersect(t); + } + return *this; +} + +UBool ScriptSet::intersects(const ScriptSet &other) const { + for (uint32_t i=0; i 0) { + count++; + x &= (x - 1); // and off the least significant one bit. + } + } + return count; +} + +int32_t ScriptSet::hashCode() const { + int32_t hash = 0; + for (int32_t i=0; i= 0; i = nextSetBit(i + 1)) { + if (!firstTime) { + dest.append((UChar)0x20); + } + firstTime = FALSE; + const char *scriptName = uscript_getShortName((UScriptCode(i))); + dest.append(UnicodeString(scriptName, -1, US_INV)); + } + return dest; +} + +ScriptSet &ScriptSet::parseScripts(const UnicodeString &scriptString, UErrorCode &status) { + resetAll(); + if (U_FAILURE(status)) { + return *this; + } + UnicodeString oneScriptName; + for (int32_t i=0; i 0) { + char buf[40]; + oneScriptName.extract(0, oneScriptName.length(), buf, sizeof(buf)-1, US_INV); + buf[sizeof(buf)-1] = 0; + int32_t sc = u_getPropertyValueEnum(UCHAR_SCRIPT, buf); + if (sc == UCHAR_INVALID_CODE) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } else { + this->set((UScriptCode)sc, status); + } + if (U_FAILURE(status)) { + return *this; + } + oneScriptName.remove(); + } + } + return *this; +} + +void ScriptSet::setScriptExtensions(UChar32 codePoint, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + static const int32_t FIRST_GUESS_SCRIPT_CAPACITY = 20; + MaybeStackArray scripts; + UErrorCode internalStatus = U_ZERO_ERROR; + int32_t script_count = -1; + + while (TRUE) { + script_count = uscript_getScriptExtensions( + codePoint, scripts.getAlias(), scripts.getCapacity(), &internalStatus); + if (internalStatus == U_BUFFER_OVERFLOW_ERROR) { + // Need to allocate more space + if (scripts.resize(script_count) == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + internalStatus = U_ZERO_ERROR; + } else { + break; + } + } + + // Check if we failed for some reason other than buffer overflow + if (U_FAILURE(internalStatus)) { + status = internalStatus; + return; + } + + // Load the scripts into the ScriptSet and return + for (int32_t i = 0; i < script_count; i++) { + this->set(scripts[i], status); + if (U_FAILURE(status)) { return; } + } +} + +U_NAMESPACE_END + +U_CAPI UBool U_EXPORT2 +uhash_equalsScriptSet(const UElement key1, const UElement key2) { + icu::ScriptSet *s1 = static_cast(key1.pointer); + icu::ScriptSet *s2 = static_cast(key2.pointer); + return (*s1 == *s2); +} + +U_CAPI int8_t U_EXPORT2 +uhash_compareScriptSet(UElement key0, UElement key1) { + icu::ScriptSet *s0 = static_cast(key0.pointer); + icu::ScriptSet *s1 = static_cast(key1.pointer); + int32_t diff = s0->countMembers() - s1->countMembers(); + if (diff != 0) return static_cast(diff); + int32_t i0 = s0->nextSetBit(0); + int32_t i1 = s1->nextSetBit(0); + while ((diff = i0-i1) == 0 && i0 > 0) { + i0 = s0->nextSetBit(i0+1); + i1 = s1->nextSetBit(i1+1); + } + return (int8_t)diff; +} + +U_CAPI int32_t U_EXPORT2 +uhash_hashScriptSet(const UElement key) { + icu::ScriptSet *s = static_cast(key.pointer); + return s->hashCode(); +} + +U_CAPI void U_EXPORT2 +uhash_deleteScriptSet(void *obj) { + icu::ScriptSet *s = static_cast(obj); + delete s; +} diff --git a/thirdparty/icu4c/i18n/scriptset.h b/thirdparty/icu4c/i18n/scriptset.h new file mode 100644 index 0000000000..51980ab7b3 --- /dev/null +++ b/thirdparty/icu4c/i18n/scriptset.h @@ -0,0 +1,86 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +********************************************************************** +* Copyright (C) 2013, International Business Machines +* Corporation and others. All Rights Reserved. +********************************************************************** +* +* scriptset.h +* +* created on: 2013 Jan 7 +* created by: Andy Heninger +*/ + +#ifndef __SCRIPTSET_H__ +#define __SCRIPTSET_H__ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" +#include "unicode/uscript.h" + +#include "uelement.h" + +U_NAMESPACE_BEGIN + +//------------------------------------------------------------------------------- +// +// ScriptSet - A bit set representing a set of scripts. +// +// This class was originally used exclusively with script sets appearing +// as part of the spoof check whole script confusable binary data. Its +// use has since become more general, but the continued use to wrap +// prebuilt binary data does constrain the design. +// +//------------------------------------------------------------------------------- +class U_I18N_API ScriptSet: public UMemory { + public: + static constexpr int32_t SCRIPT_LIMIT = 224; // multiple of 32! + + ScriptSet(); + ScriptSet(const ScriptSet &other); + ~ScriptSet(); + + bool operator == (const ScriptSet &other) const; + bool operator != (const ScriptSet &other) const {return !(*this == other);} + ScriptSet & operator = (const ScriptSet &other); + + UBool test(UScriptCode script, UErrorCode &status) const; + ScriptSet &Union(const ScriptSet &other); + ScriptSet &set(UScriptCode script, UErrorCode &status); + ScriptSet &reset(UScriptCode script, UErrorCode &status); + ScriptSet &intersect(const ScriptSet &other); + ScriptSet &intersect(UScriptCode script, UErrorCode &status); + UBool intersects(const ScriptSet &other) const; // Sets contain at least one script in common. + UBool contains(const ScriptSet &other) const; // All set bits in other are also set in this. + + ScriptSet &setAll(); + ScriptSet &resetAll(); + int32_t countMembers() const; + int32_t hashCode() const; + int32_t nextSetBit(int32_t script) const; + + UBool isEmpty() const; + + UnicodeString &displayScripts(UnicodeString &dest) const; // append script names to dest string. + ScriptSet & parseScripts(const UnicodeString &scriptsString, UErrorCode &status); // Replaces ScriptSet contents. + + // Wraps around UScript::getScriptExtensions() and adds the corresponding scripts to this instance. + void setScriptExtensions(UChar32 codePoint, UErrorCode& status); + + private: + uint32_t bits[SCRIPT_LIMIT / 32]; +}; + +U_NAMESPACE_END + +U_CAPI UBool U_EXPORT2 +uhash_compareScriptSet(const UElement key1, const UElement key2); + +U_CAPI int32_t U_EXPORT2 +uhash_hashScriptSet(const UElement key); + +U_CAPI void U_EXPORT2 +uhash_deleteScriptSet(void *obj); + +#endif // __SCRIPTSET_H__ diff --git a/thirdparty/icu4c/i18n/ucln_in.cpp b/thirdparty/icu4c/i18n/ucln_in.cpp new file mode 100644 index 0000000000..f29cbe41dd --- /dev/null +++ b/thirdparty/icu4c/i18n/ucln_in.cpp @@ -0,0 +1,65 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +****************************************************************************** +* * +* Copyright (C) 2001-2014, International Business Machines * +* Corporation and others. All Rights Reserved. * +* * +****************************************************************************** +* file name: ucln_in.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2001July05 +* created by: George Rhoten +*/ + +#include "ucln.h" +#include "ucln_in.h" +#include "mutex.h" +#include "uassert.h" + +/** Auto-client for UCLN_I18N **/ +#define UCLN_TYPE UCLN_I18N +#include "ucln_imp.h" + +/* Leave this copyright notice here! It needs to go somewhere in this library. */ +static const char copyright[] = U_COPYRIGHT_STRING; + +static cleanupFunc *gCleanupFunctions[UCLN_I18N_COUNT]; + +static UBool U_CALLCONV i18n_cleanup(void) +{ + int32_t libType = UCLN_I18N_START; + (void)copyright; /* Suppress unused variable warning with clang. */ + + while (++libType + * This class, based on Unicode Technical Report #36 and + * Unicode Technical Standard #39, has two main functions: + * + *
    + *
  1. Checking whether two strings are visually confusable with each other, such as "Harvest" and + * "Ηarvest", where the second string starts with the Greek capital letter Eta.
  2. + *
  3. Checking whether an individual string is likely to be an attempt at confusing the reader (spoof + * detection), such as "paypal" with some Latin characters substituted with Cyrillic look-alikes.
  4. + *
+ * + *

+ * Although originally designed as a method for flagging suspicious identifier strings such as URLs, + * USpoofChecker has a number of other practical use cases, such as preventing attempts to evade bad-word + * content filters. + * + *

+ * The functions of this class are exposed as C API, with a handful of syntactical conveniences for C++. + * + *

Confusables

+ * + *

+ * The following example shows how to use USpoofChecker to check for confusability between two strings: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * UChar* str1 = (UChar*) u"Harvest"; + * UChar* str2 = (UChar*) u"\u0397arvest"; // with U+0397 GREEK CAPITAL LETTER ETA + * + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status); + * + * int32_t bitmask = uspoof_areConfusable(sc, str1, -1, str2, -1, &status); + * UBool result = bitmask != 0; + * // areConfusable: 1 (status: U_ZERO_ERROR) + * printf("areConfusable: %d (status: %s)\n", result, u_errorName(status)); + * uspoof_close(sc); + * \endcode + * + *

+ * The call to {@link uspoof_open} creates a USpoofChecker object; the call to {@link uspoof_setChecks} + * enables confusable checking and disables all other checks; the call to {@link uspoof_areConfusable} performs the + * confusability test; and the following line extracts the result out of the return value. For best performance, + * the instance should be created once (e.g., upon application startup), and the efficient + * {@link uspoof_areConfusable} method can be used at runtime. + * + *

+ * The type {@link LocalUSpoofCheckerPointer} is exposed for C++ programmers. It will automatically call + * {@link uspoof_close} when the object goes out of scope: + * + * \code{.cpp} + * UErrorCode status = U_ZERO_ERROR; + * LocalUSpoofCheckerPointer sc(uspoof_open(&status)); + * uspoof_setChecks(sc.getAlias(), USPOOF_CONFUSABLE, &status); + * // ... + * \endcode + * + * UTS 39 defines two strings to be confusable if they map to the same skeleton string. A skeleton can + * be thought of as a "hash code". {@link uspoof_getSkeleton} computes the skeleton for a particular string, so + * the following snippet is equivalent to the example above: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * UChar* str1 = (UChar*) u"Harvest"; + * UChar* str2 = (UChar*) u"\u0397arvest"; // with U+0397 GREEK CAPITAL LETTER ETA + * + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status); + * + * // Get skeleton 1 + * int32_t skel1Len = uspoof_getSkeleton(sc, 0, str1, -1, NULL, 0, &status); + * UChar* skel1 = (UChar*) malloc(++skel1Len * sizeof(UChar)); + * status = U_ZERO_ERROR; + * uspoof_getSkeleton(sc, 0, str1, -1, skel1, skel1Len, &status); + * + * // Get skeleton 2 + * int32_t skel2Len = uspoof_getSkeleton(sc, 0, str2, -1, NULL, 0, &status); + * UChar* skel2 = (UChar*) malloc(++skel2Len * sizeof(UChar)); + * status = U_ZERO_ERROR; + * uspoof_getSkeleton(sc, 0, str2, -1, skel2, skel2Len, &status); + * + * // Are the skeletons the same? + * UBool result = u_strcmp(skel1, skel2) == 0; + * // areConfusable: 1 (status: U_ZERO_ERROR) + * printf("areConfusable: %d (status: %s)\n", result, u_errorName(status)); + * uspoof_close(sc); + * free(skel1); + * free(skel2); + * \endcode + * + * If you need to check if a string is confusable with any string in a dictionary of many strings, rather than calling + * {@link uspoof_areConfusable} many times in a loop, {@link uspoof_getSkeleton} can be used instead, as shown below: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * #define DICTIONARY_LENGTH 2 + * UChar* dictionary[DICTIONARY_LENGTH] = { (UChar*) u"lorem", (UChar*) u"ipsum" }; + * UChar* skeletons[DICTIONARY_LENGTH]; + * UChar* str = (UChar*) u"1orern"; + * + * // Setup: + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status); + * for (size_t i=0; iNote: Since the Unicode confusables mapping table is frequently updated, confusable skeletons are not + * guaranteed to be the same between ICU releases. We therefore recommend that you always compute confusable skeletons + * at runtime and do not rely on creating a permanent, or difficult to update, database of skeletons. + * + *

Spoof Detection

+ * + * The following snippet shows a minimal example of using USpoofChecker to perform spoof detection on a + * string: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * UChar* str = (UChar*) u"p\u0430ypal"; // with U+0430 CYRILLIC SMALL LETTER A + * + * // Get the default set of allowable characters: + * USet* allowed = uset_openEmpty(); + * uset_addAll(allowed, uspoof_getRecommendedSet(&status)); + * uset_addAll(allowed, uspoof_getInclusionSet(&status)); + * + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setAllowedChars(sc, allowed, &status); + * uspoof_setRestrictionLevel(sc, USPOOF_MODERATELY_RESTRICTIVE); + * + * int32_t bitmask = uspoof_check(sc, str, -1, NULL, &status); + * UBool result = bitmask != 0; + * // fails checks: 1 (status: U_ZERO_ERROR) + * printf("fails checks: %d (status: %s)\n", result, u_errorName(status)); + * uspoof_close(sc); + * uset_close(allowed); + * \endcode + * + * As in the case for confusability checking, it is good practice to create one USpoofChecker instance at + * startup, and call the cheaper {@link uspoof_check} online. We specify the set of + * allowed characters to be those with type RECOMMENDED or INCLUSION, according to the recommendation in UTS 39. + * + * In addition to {@link uspoof_check}, the function {@link uspoof_checkUTF8} is exposed for UTF8-encoded char* strings, + * and {@link uspoof_checkUnicodeString} is exposed for C++ programmers. + * + * If the {@link USPOOF_AUX_INFO} check is enabled, a limited amount of information on why a string failed the checks + * is available in the returned bitmask. For complete information, use the {@link uspoof_check2} class of functions + * with a {@link USpoofCheckResult} parameter: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * UChar* str = (UChar*) u"p\u0430ypal"; // with U+0430 CYRILLIC SMALL LETTER A + * + * // Get the default set of allowable characters: + * USet* allowed = uset_openEmpty(); + * uset_addAll(allowed, uspoof_getRecommendedSet(&status)); + * uset_addAll(allowed, uspoof_getInclusionSet(&status)); + * + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setAllowedChars(sc, allowed, &status); + * uspoof_setRestrictionLevel(sc, USPOOF_MODERATELY_RESTRICTIVE); + * + * USpoofCheckResult* checkResult = uspoof_openCheckResult(&status); + * int32_t bitmask = uspoof_check2(sc, str, -1, checkResult, &status); + * + * int32_t failures1 = bitmask; + * int32_t failures2 = uspoof_getCheckResultChecks(checkResult, &status); + * assert(failures1 == failures2); + * // checks that failed: 0x00000010 (status: U_ZERO_ERROR) + * printf("checks that failed: %#010x (status: %s)\n", failures1, u_errorName(status)); + * + * // Cleanup: + * uspoof_close(sc); + * uset_close(allowed); + * uspoof_closeCheckResult(checkResult); + * \endcode + * + * C++ users can take advantage of a few syntactical conveniences. The following snippet is functionally + * equivalent to the one above: + * + * \code{.cpp} + * UErrorCode status = U_ZERO_ERROR; + * UnicodeString str((UChar*) u"p\u0430ypal"); // with U+0430 CYRILLIC SMALL LETTER A + * + * // Get the default set of allowable characters: + * UnicodeSet allowed; + * allowed.addAll(*uspoof_getRecommendedUnicodeSet(&status)); + * allowed.addAll(*uspoof_getInclusionUnicodeSet(&status)); + * + * LocalUSpoofCheckerPointer sc(uspoof_open(&status)); + * uspoof_setAllowedChars(sc.getAlias(), allowed.toUSet(), &status); + * uspoof_setRestrictionLevel(sc.getAlias(), USPOOF_MODERATELY_RESTRICTIVE); + * + * LocalUSpoofCheckResultPointer checkResult(uspoof_openCheckResult(&status)); + * int32_t bitmask = uspoof_check2UnicodeString(sc.getAlias(), str, checkResult.getAlias(), &status); + * + * int32_t failures1 = bitmask; + * int32_t failures2 = uspoof_getCheckResultChecks(checkResult.getAlias(), &status); + * assert(failures1 == failures2); + * // checks that failed: 0x00000010 (status: U_ZERO_ERROR) + * printf("checks that failed: %#010x (status: %s)\n", failures1, u_errorName(status)); + * + * // Explicit cleanup not necessary. + * \endcode + * + * The return value is a bitmask of the checks that failed. In this case, there was one check that failed: + * {@link USPOOF_RESTRICTION_LEVEL}, corresponding to the fifth bit (16). The possible checks are: + * + *
    + *
  • RESTRICTION_LEVEL: flags strings that violate the + * Restriction Level test as specified in UTS + * 39; in most cases, this means flagging strings that contain characters from multiple different scripts.
  • + *
  • INVISIBLE: flags strings that contain invisible characters, such as zero-width spaces, or character + * sequences that are likely not to display, such as multiple occurrences of the same non-spacing mark.
  • + *
  • CHAR_LIMIT: flags strings that contain characters outside of a specified set of acceptable + * characters. See {@link uspoof_setAllowedChars} and {@link uspoof_setAllowedLocales}.
  • + *
  • MIXED_NUMBERS: flags strings that contain digits from multiple different numbering systems.
  • + *
+ * + *

+ * These checks can be enabled independently of each other. For example, if you were interested in checking for only the + * INVISIBLE and MIXED_NUMBERS conditions, you could do: + * + * \code{.c} + * UErrorCode status = U_ZERO_ERROR; + * UChar* str = (UChar*) u"8\u09EA"; // 8 mixed with U+09EA BENGALI DIGIT FOUR + * + * USpoofChecker* sc = uspoof_open(&status); + * uspoof_setChecks(sc, USPOOF_INVISIBLE | USPOOF_MIXED_NUMBERS, &status); + * + * int32_t bitmask = uspoof_check2(sc, str, -1, NULL, &status); + * UBool result = bitmask != 0; + * // fails checks: 1 (status: U_ZERO_ERROR) + * printf("fails checks: %d (status: %s)\n", result, u_errorName(status)); + * uspoof_close(sc); + * \endcode + * + * Here is an example in C++ showing how to compute the restriction level of a string: + * + * \code{.cpp} + * UErrorCode status = U_ZERO_ERROR; + * UnicodeString str((UChar*) u"p\u0430ypal"); // with U+0430 CYRILLIC SMALL LETTER A + * + * // Get the default set of allowable characters: + * UnicodeSet allowed; + * allowed.addAll(*uspoof_getRecommendedUnicodeSet(&status)); + * allowed.addAll(*uspoof_getInclusionUnicodeSet(&status)); + * + * LocalUSpoofCheckerPointer sc(uspoof_open(&status)); + * uspoof_setAllowedChars(sc.getAlias(), allowed.toUSet(), &status); + * uspoof_setRestrictionLevel(sc.getAlias(), USPOOF_MODERATELY_RESTRICTIVE); + * uspoof_setChecks(sc.getAlias(), USPOOF_RESTRICTION_LEVEL | USPOOF_AUX_INFO, &status); + * + * LocalUSpoofCheckResultPointer checkResult(uspoof_openCheckResult(&status)); + * int32_t bitmask = uspoof_check2UnicodeString(sc.getAlias(), str, checkResult.getAlias(), &status); + * + * URestrictionLevel restrictionLevel = uspoof_getCheckResultRestrictionLevel(checkResult.getAlias(), &status); + * // Since USPOOF_AUX_INFO was enabled, the restriction level is also available in the upper bits of the bitmask: + * assert((restrictionLevel & bitmask) == restrictionLevel); + * // Restriction level: 0x50000000 (status: U_ZERO_ERROR) + * printf("Restriction level: %#010x (status: %s)\n", restrictionLevel, u_errorName(status)); + * \endcode + * + * The code '0x50000000' corresponds to the restriction level USPOOF_MINIMALLY_RESTRICTIVE. Since + * USPOOF_MINIMALLY_RESTRICTIVE is weaker than USPOOF_MODERATELY_RESTRICTIVE, the string fails the check. + * + * Note: The Restriction Level is the most powerful of the checks. The full logic is documented in + * UTS 39, but the basic idea is that strings + * are restricted to contain characters from only a single script, except that most scripts are allowed to have + * Latin characters interspersed. Although the default restriction level is HIGHLY_RESTRICTIVE, it is + * recommended that users set their restriction level to MODERATELY_RESTRICTIVE, which allows Latin mixed + * with all other scripts except Cyrillic, Greek, and Cherokee, with which it is often confusable. For more details on + * the levels, see UTS 39 or {@link URestrictionLevel}. The Restriction Level test is aware of the set of + * allowed characters set in {@link uspoof_setAllowedChars}. Note that characters which have script code + * COMMON or INHERITED, such as numbers and punctuation, are ignored when computing whether a string has multiple + * scripts. + * + *

Additional Information

+ * + * A USpoofChecker instance may be used repeatedly to perform checks on any number of identifiers. + * + * Thread Safety: The test functions for checking a single identifier, or for testing whether + * two identifiers are possible confusable, are thread safe. They may called concurrently, from multiple threads, + * using the same USpoofChecker instance. + * + * More generally, the standard ICU thread safety rules apply: functions that take a const USpoofChecker parameter are + * thread safe. Those that take a non-const USpoofChecker are not thread safe.. + * + * @stable ICU 4.6 + */ + +U_CDECL_BEGIN + +struct USpoofChecker; +/** + * @stable ICU 4.2 + */ +typedef struct USpoofChecker USpoofChecker; /**< typedef for C of USpoofChecker */ + +struct USpoofCheckResult; +/** + * @see uspoof_openCheckResult + * @stable ICU 58 + */ +typedef struct USpoofCheckResult USpoofCheckResult; + +/** + * Enum for the kinds of checks that USpoofChecker can perform. + * These enum values are used both to select the set of checks that + * will be performed, and to report results from the check function. + * + * @stable ICU 4.2 + */ +typedef enum USpoofChecks { + /** + * When performing the two-string {@link uspoof_areConfusable} test, this flag in the return value indicates + * that the two strings are visually confusable and that they are from the same script, according to UTS 39 section + * 4. + * + * @see uspoof_areConfusable + * @stable ICU 4.2 + */ + USPOOF_SINGLE_SCRIPT_CONFUSABLE = 1, + + /** + * When performing the two-string {@link uspoof_areConfusable} test, this flag in the return value indicates + * that the two strings are visually confusable and that they are not from the same script, according to UTS + * 39 section 4. + * + * @see uspoof_areConfusable + * @stable ICU 4.2 + */ + USPOOF_MIXED_SCRIPT_CONFUSABLE = 2, + + /** + * When performing the two-string {@link uspoof_areConfusable} test, this flag in the return value indicates + * that the two strings are visually confusable and that they are not from the same script but both of them are + * single-script strings, according to UTS 39 section 4. + * + * @see uspoof_areConfusable + * @stable ICU 4.2 + */ + USPOOF_WHOLE_SCRIPT_CONFUSABLE = 4, + + /** + * Enable this flag in {@link uspoof_setChecks} to turn on all types of confusables. You may set + * the checks to some subset of SINGLE_SCRIPT_CONFUSABLE, MIXED_SCRIPT_CONFUSABLE, or WHOLE_SCRIPT_CONFUSABLE to + * make {@link uspoof_areConfusable} return only those types of confusables. + * + * @see uspoof_areConfusable + * @see uspoof_getSkeleton + * @stable ICU 58 + */ + USPOOF_CONFUSABLE = USPOOF_SINGLE_SCRIPT_CONFUSABLE | USPOOF_MIXED_SCRIPT_CONFUSABLE | USPOOF_WHOLE_SCRIPT_CONFUSABLE, + +#ifndef U_HIDE_DEPRECATED_API + /** + * This flag is deprecated and no longer affects the behavior of SpoofChecker. + * + * @deprecated ICU 58 Any case confusable mappings were removed from UTS 39; the corresponding ICU API was deprecated. + */ + USPOOF_ANY_CASE = 8, +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Check that an identifier is no looser than the specified RestrictionLevel. + * The default if {@link uspoof_setRestrictionLevel} is not called is HIGHLY_RESTRICTIVE. + * + * If USPOOF_AUX_INFO is enabled the actual restriction level of the + * identifier being tested will also be returned by uspoof_check(). + * + * @see URestrictionLevel + * @see uspoof_setRestrictionLevel + * @see USPOOF_AUX_INFO + * + * @stable ICU 51 + */ + USPOOF_RESTRICTION_LEVEL = 16, + +#ifndef U_HIDE_DEPRECATED_API + /** Check that an identifier contains only characters from a + * single script (plus chars from the common and inherited scripts.) + * Applies to checks of a single identifier check only. + * @deprecated ICU 51 Use RESTRICTION_LEVEL instead. + */ + USPOOF_SINGLE_SCRIPT = USPOOF_RESTRICTION_LEVEL, +#endif /* U_HIDE_DEPRECATED_API */ + + /** Check an identifier for the presence of invisible characters, + * such as zero-width spaces, or character sequences that are + * likely not to display, such as multiple occurrences of the same + * non-spacing mark. This check does not test the input string as a whole + * for conformance to any particular syntax for identifiers. + */ + USPOOF_INVISIBLE = 32, + + /** Check that an identifier contains only characters from a specified set + * of acceptable characters. See {@link uspoof_setAllowedChars} and + * {@link uspoof_setAllowedLocales}. Note that a string that fails this check + * will also fail the {@link USPOOF_RESTRICTION_LEVEL} check. + */ + USPOOF_CHAR_LIMIT = 64, + + /** + * Check that an identifier does not mix numbers from different numbering systems. + * For more information, see UTS 39 section 5.3. + * + * @stable ICU 51 + */ + USPOOF_MIXED_NUMBERS = 128, + + /** + * Check that an identifier does not have a combining character following a character in which that + * combining character would be hidden; for example 'i' followed by a U+0307 combining dot. + * + * More specifically, the following characters are forbidden from preceding a U+0307: + *
    + *
  • Those with the Soft_Dotted Unicode property (which includes 'i' and 'j')
  • + *
  • Latin lowercase letter 'l'
  • + *
  • Dotless 'i' and 'j' ('ı' and 'ȷ', U+0131 and U+0237)
  • + *
  • Any character whose confusable prototype ends with such a character + * (Soft_Dotted, 'l', 'ı', or 'ȷ')
  • + *
+ * In addition, combining characters are allowed between the above characters and U+0307 except those + * with combining class 0 or combining class "Above" (230, same class as U+0307). + * + * This list and the number of combing characters considered by this check may grow over time. + * + * @stable ICU 62 + */ + USPOOF_HIDDEN_OVERLAY = 256, + + /** + * Enable all spoof checks. + * + * @stable ICU 4.6 + */ + USPOOF_ALL_CHECKS = 0xFFFF, + + /** + * Enable the return of auxiliary (non-error) information in the + * upper bits of the check results value. + * + * If this "check" is not enabled, the results of {@link uspoof_check} will be + * zero when an identifier passes all of the enabled checks. + * + * If this "check" is enabled, (uspoof_check() & {@link USPOOF_ALL_CHECKS}) will + * be zero when an identifier passes all checks. + * + * @stable ICU 51 + */ + USPOOF_AUX_INFO = 0x40000000 + + } USpoofChecks; + + + /** + * Constants from UAX #39 for use in {@link uspoof_setRestrictionLevel}, and + * for returned identifier restriction levels in check results. + * + * @stable ICU 51 + * + * @see uspoof_setRestrictionLevel + * @see uspoof_check + */ + typedef enum URestrictionLevel { + /** + * All characters in the string are in the identifier profile and all characters in the string are in the + * ASCII range. + * + * @stable ICU 51 + */ + USPOOF_ASCII = 0x10000000, + /** + * The string classifies as ASCII-Only, or all characters in the string are in the identifier profile and + * the string is single-script, according to the definition in UTS 39 section 5.1. + * + * @stable ICU 53 + */ + USPOOF_SINGLE_SCRIPT_RESTRICTIVE = 0x20000000, + /** + * The string classifies as Single Script, or all characters in the string are in the identifier profile and + * the string is covered by any of the following sets of scripts, according to the definition in UTS 39 + * section 5.1: + *
    + *
  • Latin + Han + Bopomofo (or equivalently: Latn + Hanb)
  • + *
  • Latin + Han + Hiragana + Katakana (or equivalently: Latn + Jpan)
  • + *
  • Latin + Han + Hangul (or equivalently: Latn +Kore)
  • + *
+ * This is the default restriction in ICU. + * + * @stable ICU 51 + */ + USPOOF_HIGHLY_RESTRICTIVE = 0x30000000, + /** + * The string classifies as Highly Restrictive, or all characters in the string are in the identifier profile + * and the string is covered by Latin and any one other Recommended or Aspirational script, except Cyrillic, + * Greek, and Cherokee. + * + * @stable ICU 51 + */ + USPOOF_MODERATELY_RESTRICTIVE = 0x40000000, + /** + * All characters in the string are in the identifier profile. Allow arbitrary mixtures of scripts. + * + * @stable ICU 51 + */ + USPOOF_MINIMALLY_RESTRICTIVE = 0x50000000, + /** + * Any valid identifiers, including characters outside of the Identifier Profile. + * + * @stable ICU 51 + */ + USPOOF_UNRESTRICTIVE = 0x60000000, + /** + * Mask for selecting the Restriction Level bits from the return value of {@link uspoof_check}. + * + * @stable ICU 53 + */ + USPOOF_RESTRICTION_LEVEL_MASK = 0x7F000000, +#ifndef U_HIDE_INTERNAL_API + /** + * An undefined restriction level. + * @internal + */ + USPOOF_UNDEFINED_RESTRICTIVE = -1 +#endif /* U_HIDE_INTERNAL_API */ + } URestrictionLevel; + +/** + * Create a Unicode Spoof Checker, configured to perform all + * checks except for USPOOF_LOCALE_LIMIT and USPOOF_CHAR_LIMIT. + * Note that additional checks may be added in the future, + * resulting in the changes to the default checking behavior. + * + * @param status The error code, set if this function encounters a problem. + * @return the newly created Spoof Checker + * @stable ICU 4.2 + */ +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_open(UErrorCode *status); + + +/** + * Open a Spoof checker from its serialized form, stored in 32-bit-aligned memory. + * Inverse of uspoof_serialize(). + * The memory containing the serialized data must remain valid and unchanged + * as long as the spoof checker, or any cloned copies of the spoof checker, + * are in use. Ownership of the memory remains with the caller. + * The spoof checker (and any clones) must be closed prior to deleting the + * serialized data. + * + * @param data a pointer to 32-bit-aligned memory containing the serialized form of spoof data + * @param length the number of bytes available at data; + * can be more than necessary + * @param pActualLength receives the actual number of bytes at data taken up by the data; + * can be NULL + * @param pErrorCode ICU error code + * @return the spoof checker. + * + * @see uspoof_open + * @see uspoof_serialize + * @stable ICU 4.2 + */ +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_openFromSerialized(const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *pErrorCode); + +/** + * Open a Spoof Checker from the source form of the spoof data. + * The input corresponds to the Unicode data file confusables.txt + * as described in Unicode UAX #39. The syntax of the source data + * is as described in UAX #39 for this file, and the content of + * this file is acceptable input. + * + * The character encoding of the (char *) input text is UTF-8. + * + * @param confusables a pointer to the confusable characters definitions, + * as found in file confusables.txt from unicode.org. + * @param confusablesLen The length of the confusables text, or -1 if the + * input string is zero terminated. + * @param confusablesWholeScript + * Deprecated in ICU 58. No longer used. + * @param confusablesWholeScriptLen + * Deprecated in ICU 58. No longer used. + * @param errType In the event of an error in the input, indicates + * which of the input files contains the error. + * The value is one of USPOOF_SINGLE_SCRIPT_CONFUSABLE or + * USPOOF_WHOLE_SCRIPT_CONFUSABLE, or + * zero if no errors are found. + * @param pe In the event of an error in the input, receives the position + * in the input text (line, offset) of the error. + * @param status an in/out ICU UErrorCode. Among the possible errors is + * U_PARSE_ERROR, which is used to report syntax errors + * in the input. + * @return A spoof checker that uses the rules from the input files. + * @stable ICU 4.2 + */ +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_openFromSource(const char *confusables, int32_t confusablesLen, + const char *confusablesWholeScript, int32_t confusablesWholeScriptLen, + int32_t *errType, UParseError *pe, UErrorCode *status); + + +/** + * Close a Spoof Checker, freeing any memory that was being held by + * its implementation. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uspoof_close(USpoofChecker *sc); + +/** + * Clone a Spoof Checker. The clone will be set to perform the same checks + * as the original source. + * + * @param sc The source USpoofChecker + * @param status The error code, set if this function encounters a problem. + * @return + * @stable ICU 4.2 + */ +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_clone(const USpoofChecker *sc, UErrorCode *status); + + +/** + * Specify the bitmask of checks that will be performed by {@link uspoof_check}. Calling this method + * overwrites any checks that may have already been enabled. By default, all checks are enabled. + * + * To enable specific checks and disable all others, + * OR together only the bit constants for the desired checks. + * For example, to fail strings containing characters outside of + * the set specified by {@link uspoof_setAllowedChars} and + * also strings that contain digits from mixed numbering systems: + * + *
+ * {@code
+ * uspoof_setChecks(USPOOF_CHAR_LIMIT | USPOOF_MIXED_NUMBERS);
+ * }
+ * 
+ * + * To disable specific checks and enable all others, + * start with ALL_CHECKS and "AND away" the not-desired checks. + * For example, if you are not planning to use the {@link uspoof_areConfusable} functionality, + * it is good practice to disable the CONFUSABLE check: + * + *
+ * {@code
+ * uspoof_setChecks(USPOOF_ALL_CHECKS & ~USPOOF_CONFUSABLE);
+ * }
+ * 
+ * + * Note that methods such as {@link uspoof_setAllowedChars}, {@link uspoof_setAllowedLocales}, and + * {@link uspoof_setRestrictionLevel} will enable certain checks when called. Those methods will OR the check they + * enable onto the existing bitmask specified by this method. For more details, see the documentation of those + * methods. + * + * @param sc The USpoofChecker + * @param checks The set of checks that this spoof checker will perform. + * The value is a bit set, obtained by OR-ing together + * values from enum USpoofChecks. + * @param status The error code, set if this function encounters a problem. + * @stable ICU 4.2 + * + */ +U_CAPI void U_EXPORT2 +uspoof_setChecks(USpoofChecker *sc, int32_t checks, UErrorCode *status); + +/** + * Get the set of checks that this Spoof Checker has been configured to perform. + * + * @param sc The USpoofChecker + * @param status The error code, set if this function encounters a problem. + * @return The set of checks that this spoof checker will perform. + * The value is a bit set, obtained by OR-ing together + * values from enum USpoofChecks. + * @stable ICU 4.2 + * + */ +U_CAPI int32_t U_EXPORT2 +uspoof_getChecks(const USpoofChecker *sc, UErrorCode *status); + +/** + * Set the loosest restriction level allowed for strings. The default if this is not called is + * {@link USPOOF_HIGHLY_RESTRICTIVE}. Calling this method enables the {@link USPOOF_RESTRICTION_LEVEL} and + * {@link USPOOF_MIXED_NUMBERS} checks, corresponding to Sections 5.1 and 5.2 of UTS 39. To customize which checks are + * to be performed by {@link uspoof_check}, see {@link uspoof_setChecks}. + * + * @param sc The USpoofChecker + * @param restrictionLevel The loosest restriction level allowed. + * @see URestrictionLevel + * @stable ICU 51 + */ +U_CAPI void U_EXPORT2 +uspoof_setRestrictionLevel(USpoofChecker *sc, URestrictionLevel restrictionLevel); + + +/** + * Get the Restriction Level that will be tested if the checks include {@link USPOOF_RESTRICTION_LEVEL}. + * + * @return The restriction level + * @see URestrictionLevel + * @stable ICU 51 + */ +U_CAPI URestrictionLevel U_EXPORT2 +uspoof_getRestrictionLevel(const USpoofChecker *sc); + +/** + * Limit characters that are acceptable in identifiers being checked to those + * normally used with the languages associated with the specified locales. + * Any previously specified list of locales is replaced by the new settings. + * + * A set of languages is determined from the locale(s), and + * from those a set of acceptable Unicode scripts is determined. + * Characters from this set of scripts, along with characters from + * the "common" and "inherited" Unicode Script categories + * will be permitted. + * + * Supplying an empty string removes all restrictions; + * characters from any script will be allowed. + * + * The {@link USPOOF_CHAR_LIMIT} test is automatically enabled for this + * USpoofChecker when calling this function with a non-empty list + * of locales. + * + * The Unicode Set of characters that will be allowed is accessible + * via the uspoof_getAllowedChars() function. uspoof_setAllowedLocales() + * will replace any previously applied set of allowed characters. + * + * Adjustments, such as additions or deletions of certain classes of characters, + * can be made to the result of uspoof_setAllowedLocales() by + * fetching the resulting set with uspoof_getAllowedChars(), + * manipulating it with the Unicode Set API, then resetting the + * spoof detectors limits with uspoof_setAllowedChars(). + * + * @param sc The USpoofChecker + * @param localesList A list list of locales, from which the language + * and associated script are extracted. The locales + * are comma-separated if there is more than one. + * White space may not appear within an individual locale, + * but is ignored otherwise. + * The locales are syntactically like those from the + * HTTP Accept-Language header. + * If the localesList is empty, no restrictions will be placed on + * the allowed characters. + * + * @param status The error code, set if this function encounters a problem. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uspoof_setAllowedLocales(USpoofChecker *sc, const char *localesList, UErrorCode *status); + +/** + * Get a list of locales for the scripts that are acceptable in strings + * to be checked. If no limitations on scripts have been specified, + * an empty string will be returned. + * + * uspoof_setAllowedChars() will reset the list of allowed to be empty. + * + * The format of the returned list is the same as that supplied to + * uspoof_setAllowedLocales(), but returned list may not be identical + * to the originally specified string; the string may be reformatted, + * and information other than languages from + * the originally specified locales may be omitted. + * + * @param sc The USpoofChecker + * @param status The error code, set if this function encounters a problem. + * @return A string containing a list of locales corresponding + * to the acceptable scripts, formatted like an + * HTTP Accept Language value. + * + * @stable ICU 4.2 + */ +U_CAPI const char * U_EXPORT2 +uspoof_getAllowedLocales(USpoofChecker *sc, UErrorCode *status); + + +/** + * Limit the acceptable characters to those specified by a Unicode Set. + * Any previously specified character limit is + * is replaced by the new settings. This includes limits on + * characters that were set with the uspoof_setAllowedLocales() function. + * + * The USPOOF_CHAR_LIMIT test is automatically enabled for this + * USpoofChecker by this function. + * + * @param sc The USpoofChecker + * @param chars A Unicode Set containing the list of + * characters that are permitted. Ownership of the set + * remains with the caller. The incoming set is cloned by + * this function, so there are no restrictions on modifying + * or deleting the USet after calling this function. + * @param status The error code, set if this function encounters a problem. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uspoof_setAllowedChars(USpoofChecker *sc, const USet *chars, UErrorCode *status); + + +/** + * Get a USet for the characters permitted in an identifier. + * This corresponds to the limits imposed by the Set Allowed Characters + * functions. Limitations imposed by other checks will not be + * reflected in the set returned by this function. + * + * The returned set will be frozen, meaning that it cannot be modified + * by the caller. + * + * Ownership of the returned set remains with the Spoof Detector. The + * returned set will become invalid if the spoof detector is closed, + * or if a new set of allowed characters is specified. + * + * + * @param sc The USpoofChecker + * @param status The error code, set if this function encounters a problem. + * @return A USet containing the characters that are permitted by + * the USPOOF_CHAR_LIMIT test. + * @stable ICU 4.2 + */ +U_CAPI const USet * U_EXPORT2 +uspoof_getAllowedChars(const USpoofChecker *sc, UErrorCode *status); + + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * \note + * Consider using the newer API, {@link uspoof_check2}, instead. + * The newer API exposes additional information from the check procedure + * and is otherwise identical to this method. + * + * @param sc The USpoofChecker + * @param id The identifier to be checked for possible security issues, + * in UTF-16 format. + * @param length the length of the string to be checked, expressed in + * 16 bit UTF-16 code units, or -1 if the string is + * zero terminated. + * @param position Deprecated in ICU 51. Always returns zero. + * Originally, an out parameter for the index of the first + * string position that failed a check. + * This parameter may be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. + * @see uspoof_check2 + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_check(const USpoofChecker *sc, + const UChar *id, int32_t length, + int32_t *position, + UErrorCode *status); + + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * \note + * Consider using the newer API, {@link uspoof_check2UTF8}, instead. + * The newer API exposes additional information from the check procedure + * and is otherwise identical to this method. + * + * @param sc The USpoofChecker + * @param id A identifier to be checked for possible security issues, in UTF8 format. + * @param length the length of the string to be checked, or -1 if the string is + * zero terminated. + * @param position Deprecated in ICU 51. Always returns zero. + * Originally, an out parameter for the index of the first + * string position that failed a check. + * This parameter may be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * If the input contains invalid UTF-8 sequences, + * a status of U_INVALID_CHAR_FOUND will be returned. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. + * @see uspoof_check2UTF8 + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_checkUTF8(const USpoofChecker *sc, + const char *id, int32_t length, + int32_t *position, + UErrorCode *status); + + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * @param sc The USpoofChecker + * @param id The identifier to be checked for possible security issues, + * in UTF-16 format. + * @param length the length of the string to be checked, or -1 if the string is + * zero terminated. + * @param checkResult An instance of USpoofCheckResult to be filled with + * details about the identifier. Can be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. Any information in this bitmask will be + * consistent with the information saved in the optional + * checkResult parameter. + * @see uspoof_openCheckResult + * @see uspoof_check2UTF8 + * @see uspoof_check2UnicodeString + * @stable ICU 58 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_check2(const USpoofChecker *sc, + const UChar* id, int32_t length, + USpoofCheckResult* checkResult, + UErrorCode *status); + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * This version of {@link uspoof_check} accepts a USpoofCheckResult, which + * returns additional information about the identifier. For more + * information, see {@link uspoof_openCheckResult}. + * + * @param sc The USpoofChecker + * @param id A identifier to be checked for possible security issues, in UTF8 format. + * @param length the length of the string to be checked, or -1 if the string is + * zero terminated. + * @param checkResult An instance of USpoofCheckResult to be filled with + * details about the identifier. Can be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. Any information in this bitmask will be + * consistent with the information saved in the optional + * checkResult parameter. + * @see uspoof_openCheckResult + * @see uspoof_check2 + * @see uspoof_check2UnicodeString + * @stable ICU 58 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_check2UTF8(const USpoofChecker *sc, + const char *id, int32_t length, + USpoofCheckResult* checkResult, + UErrorCode *status); + +/** + * Create a USpoofCheckResult, used by the {@link uspoof_check2} class of functions to return + * information about the identifier. Information includes: + *
    + *
  • A bitmask of the checks that failed
  • + *
  • The identifier's restriction level (UTS 39 section 5.2)
  • + *
  • The set of numerics in the string (UTS 39 section 5.3)
  • + *
+ * The data held in a USpoofCheckResult is cleared whenever it is passed into a new call + * of {@link uspoof_check2}. + * + * @param status The error code, set if this function encounters a problem. + * @return the newly created USpoofCheckResult + * @see uspoof_check2 + * @see uspoof_check2UTF8 + * @see uspoof_check2UnicodeString + * @stable ICU 58 + */ +U_CAPI USpoofCheckResult* U_EXPORT2 +uspoof_openCheckResult(UErrorCode *status); + +/** + * Close a USpoofCheckResult, freeing any memory that was being held by + * its implementation. + * + * @param checkResult The instance of USpoofCheckResult to close + * @stable ICU 58 + */ +U_CAPI void U_EXPORT2 +uspoof_closeCheckResult(USpoofCheckResult *checkResult); + +/** + * Indicates which of the spoof check(s) have failed. The value is a bitwise OR of the constants for the tests + * in question: USPOOF_RESTRICTION_LEVEL, USPOOF_CHAR_LIMIT, and so on. + * + * @param checkResult The instance of USpoofCheckResult created by {@link uspoof_openCheckResult} + * @param status The error code, set if an error occurred. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. + * @see uspoof_setChecks + * @stable ICU 58 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_getCheckResultChecks(const USpoofCheckResult *checkResult, UErrorCode *status); + +/** + * Gets the restriction level that the text meets, if the USPOOF_RESTRICTION_LEVEL check + * was enabled; otherwise, undefined. + * + * @param checkResult The instance of USpoofCheckResult created by {@link uspoof_openCheckResult} + * @param status The error code, set if an error occurred. + * @return The restriction level contained in the USpoofCheckResult + * @see uspoof_setRestrictionLevel + * @stable ICU 58 + */ +U_CAPI URestrictionLevel U_EXPORT2 +uspoof_getCheckResultRestrictionLevel(const USpoofCheckResult *checkResult, UErrorCode *status); + +/** + * Gets the set of numerics found in the string, if the USPOOF_MIXED_NUMBERS check was enabled; + * otherwise, undefined. The set will contain the zero digit from each decimal number system found + * in the input string. Ownership of the returned USet remains with the USpoofCheckResult. + * The USet will be free'd when {@link uspoof_closeCheckResult} is called. + * + * @param checkResult The instance of USpoofCheckResult created by {@link uspoof_openCheckResult} + * @return The set of numerics contained in the USpoofCheckResult + * @param status The error code, set if an error occurred. + * @stable ICU 58 + */ +U_CAPI const USet* U_EXPORT2 +uspoof_getCheckResultNumerics(const USpoofCheckResult *checkResult, UErrorCode *status); + + +/** + * Check the whether two specified strings are visually confusable. + * + * If the strings are confusable, the return value will be nonzero, as long as + * {@link USPOOF_CONFUSABLE} was enabled in uspoof_setChecks(). + * + * The bits in the return value correspond to flags for each of the classes of + * confusables applicable to the two input strings. According to UTS 39 + * section 4, the possible flags are: + * + *
    + *
  • {@link USPOOF_SINGLE_SCRIPT_CONFUSABLE}
  • + *
  • {@link USPOOF_MIXED_SCRIPT_CONFUSABLE}
  • + *
  • {@link USPOOF_WHOLE_SCRIPT_CONFUSABLE}
  • + *
+ * + * If one or more of the above flags were not listed in uspoof_setChecks(), this + * function will never report that class of confusable. The check + * {@link USPOOF_CONFUSABLE} enables all three flags. + * + * + * @param sc The USpoofChecker + * @param id1 The first of the two identifiers to be compared for + * confusability. The strings are in UTF-16 format. + * @param length1 the length of the first identifier, expressed in + * 16 bit UTF-16 code units, or -1 if the string is + * nul terminated. + * @param id2 The second of the two identifiers to be compared for + * confusability. The identifiers are in UTF-16 format. + * @param length2 The length of the second identifiers, expressed in + * 16 bit UTF-16 code units, or -1 if the string is + * nul terminated. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Confusability of the identifiers is not reported here, + * but through this function's return value. + * @return An integer value with bit(s) set corresponding to + * the type of confusability found, as defined by + * enum USpoofChecks. Zero is returned if the identifiers + * are not confusable. + * + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusable(const USpoofChecker *sc, + const UChar *id1, int32_t length1, + const UChar *id2, int32_t length2, + UErrorCode *status); + + + +/** + * A version of {@link uspoof_areConfusable} accepting strings in UTF-8 format. + * + * @param sc The USpoofChecker + * @param id1 The first of the two identifiers to be compared for + * confusability. The strings are in UTF-8 format. + * @param length1 the length of the first identifiers, in bytes, or -1 + * if the string is nul terminated. + * @param id2 The second of the two identifiers to be compared for + * confusability. The strings are in UTF-8 format. + * @param length2 The length of the second string in bytes, or -1 + * if the string is nul terminated. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Confusability of the strings is not reported here, + * but through this function's return value. + * @return An integer value with bit(s) set corresponding to + * the type of confusability found, as defined by + * enum USpoofChecks. Zero is returned if the strings + * are not confusable. + * + * @stable ICU 4.2 + * + * @see uspoof_areConfusable + */ +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusableUTF8(const USpoofChecker *sc, + const char *id1, int32_t length1, + const char *id2, int32_t length2, + UErrorCode *status); + + + + +/** + * Get the "skeleton" for an identifier. + * Skeletons are a transformation of the input identifier; + * Two identifiers are confusable if their skeletons are identical. + * See Unicode UAX #39 for additional information. + * + * Using skeletons directly makes it possible to quickly check + * whether an identifier is confusable with any of some large + * set of existing identifiers, by creating an efficiently + * searchable collection of the skeletons. + * + * @param sc The USpoofChecker + * @param type Deprecated in ICU 58. You may pass any number. + * Originally, controlled which of the Unicode confusable data + * tables to use. + * @param id The input identifier whose skeleton will be computed. + * @param length The length of the input identifier, expressed in 16 bit + * UTF-16 code units, or -1 if the string is zero terminated. + * @param dest The output buffer, to receive the skeleton string. + * @param destCapacity The length of the output buffer, in 16 bit units. + * The destCapacity may be zero, in which case the function will + * return the actual length of the skeleton. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * @return The length of the skeleton string. The returned length + * is always that of the complete skeleton, even when the + * supplied buffer is too small (or of zero length) + * + * @stable ICU 4.2 + * @see uspoof_areConfusable + */ +U_CAPI int32_t U_EXPORT2 +uspoof_getSkeleton(const USpoofChecker *sc, + uint32_t type, + const UChar *id, int32_t length, + UChar *dest, int32_t destCapacity, + UErrorCode *status); + +/** + * Get the "skeleton" for an identifier. + * Skeletons are a transformation of the input identifier; + * Two identifiers are confusable if their skeletons are identical. + * See Unicode UAX #39 for additional information. + * + * Using skeletons directly makes it possible to quickly check + * whether an identifier is confusable with any of some large + * set of existing identifiers, by creating an efficiently + * searchable collection of the skeletons. + * + * @param sc The USpoofChecker + * @param type Deprecated in ICU 58. You may pass any number. + * Originally, controlled which of the Unicode confusable data + * tables to use. + * @param id The UTF-8 format identifier whose skeleton will be computed. + * @param length The length of the input string, in bytes, + * or -1 if the string is zero terminated. + * @param dest The output buffer, to receive the skeleton string. + * @param destCapacity The length of the output buffer, in bytes. + * The destCapacity may be zero, in which case the function will + * return the actual length of the skeleton. + * @param status The error code, set if an error occurred while attempting to + * perform the check. Possible Errors include U_INVALID_CHAR_FOUND + * for invalid UTF-8 sequences, and + * U_BUFFER_OVERFLOW_ERROR if the destination buffer is too small + * to hold the complete skeleton. + * @return The length of the skeleton string, in bytes. The returned length + * is always that of the complete skeleton, even when the + * supplied buffer is too small (or of zero length) + * + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_getSkeletonUTF8(const USpoofChecker *sc, + uint32_t type, + const char *id, int32_t length, + char *dest, int32_t destCapacity, + UErrorCode *status); + +/** + * Get the set of Candidate Characters for Inclusion in Identifiers, as defined + * in http://unicode.org/Public/security/latest/xidmodifications.txt + * and documented in http://www.unicode.org/reports/tr39/, Unicode Security Mechanisms. + * + * The returned set is frozen. Ownership of the set remains with the ICU library; it must not + * be deleted by the caller. + * + * @param status The error code, set if a problem occurs while creating the set. + * + * @stable ICU 51 + */ +U_CAPI const USet * U_EXPORT2 +uspoof_getInclusionSet(UErrorCode *status); + +/** + * Get the set of characters from Recommended Scripts for Inclusion in Identifiers, as defined + * in http://unicode.org/Public/security/latest/xidmodifications.txt + * and documented in http://www.unicode.org/reports/tr39/, Unicode Security Mechanisms. + * + * The returned set is frozen. Ownership of the set remains with the ICU library; it must not + * be deleted by the caller. + * + * @param status The error code, set if a problem occurs while creating the set. + * + * @stable ICU 51 + */ +U_CAPI const USet * U_EXPORT2 +uspoof_getRecommendedSet(UErrorCode *status); + +/** + * Serialize the data for a spoof detector into a chunk of memory. + * The flattened spoof detection tables can later be used to efficiently + * instantiate a new Spoof Detector. + * + * The serialized spoof checker includes only the data compiled from the + * Unicode data tables by uspoof_openFromSource(); it does not include + * include any other state or configuration that may have been set. + * + * @param sc the Spoof Detector whose data is to be serialized. + * @param data a pointer to 32-bit-aligned memory to be filled with the data, + * can be NULL if capacity==0 + * @param capacity the number of bytes available at data, + * or 0 for preflighting + * @param status an in/out ICU UErrorCode; possible errors include: + * - U_BUFFER_OVERFLOW_ERROR if the data storage block is too small for serialization + * - U_ILLEGAL_ARGUMENT_ERROR the data or capacity parameters are bad + * @return the number of bytes written or needed for the spoof data + * + * @see utrie2_openFromSerialized() + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_serialize(USpoofChecker *sc, + void *data, int32_t capacity, + UErrorCode *status); + +U_CDECL_END + +#if U_SHOW_CPLUSPLUS_API + +U_NAMESPACE_BEGIN + +/** + * \class LocalUSpoofCheckerPointer + * "Smart pointer" class, closes a USpoofChecker via uspoof_close(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 4.4 + */ +/** + * \cond + * Note: Doxygen is giving a bogus warning on this U_DEFINE_LOCAL_OPEN_POINTER. + * For now, suppress with a Doxygen cond + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUSpoofCheckerPointer, USpoofChecker, uspoof_close); +/** \endcond */ + +/** + * \class LocalUSpoofCheckResultPointer + * "Smart pointer" class, closes a USpoofCheckResult via `uspoof_closeCheckResult()`. + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 58 + */ + +/** + * \cond + * Note: Doxygen is giving a bogus warning on this U_DEFINE_LOCAL_OPEN_POINTER. + * For now, suppress with a Doxygen cond + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUSpoofCheckResultPointer, USpoofCheckResult, uspoof_closeCheckResult); +/** \endcond */ + +U_NAMESPACE_END + +/** + * Limit the acceptable characters to those specified by a Unicode Set. + * Any previously specified character limit is + * is replaced by the new settings. This includes limits on + * characters that were set with the uspoof_setAllowedLocales() function. + * + * The USPOOF_CHAR_LIMIT test is automatically enabled for this + * USoofChecker by this function. + * + * @param sc The USpoofChecker + * @param chars A Unicode Set containing the list of + * characters that are permitted. Ownership of the set + * remains with the caller. The incoming set is cloned by + * this function, so there are no restrictions on modifying + * or deleting the UnicodeSet after calling this function. + * @param status The error code, set if this function encounters a problem. + * @stable ICU 4.2 + */ +U_CAPI void U_EXPORT2 +uspoof_setAllowedUnicodeSet(USpoofChecker *sc, const icu::UnicodeSet *chars, UErrorCode *status); + + +/** + * Get a UnicodeSet for the characters permitted in an identifier. + * This corresponds to the limits imposed by the Set Allowed Characters / + * UnicodeSet functions. Limitations imposed by other checks will not be + * reflected in the set returned by this function. + * + * The returned set will be frozen, meaning that it cannot be modified + * by the caller. + * + * Ownership of the returned set remains with the Spoof Detector. The + * returned set will become invalid if the spoof detector is closed, + * or if a new set of allowed characters is specified. + * + * + * @param sc The USpoofChecker + * @param status The error code, set if this function encounters a problem. + * @return A UnicodeSet containing the characters that are permitted by + * the USPOOF_CHAR_LIMIT test. + * @stable ICU 4.2 + */ +U_CAPI const icu::UnicodeSet * U_EXPORT2 +uspoof_getAllowedUnicodeSet(const USpoofChecker *sc, UErrorCode *status); + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * \note + * Consider using the newer API, {@link uspoof_check2UnicodeString}, instead. + * The newer API exposes additional information from the check procedure + * and is otherwise identical to this method. + * + * @param sc The USpoofChecker + * @param id A identifier to be checked for possible security issues. + * @param position Deprecated in ICU 51. Always returns zero. + * Originally, an out parameter for the index of the first + * string position that failed a check. + * This parameter may be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. + * @see uspoof_check2UnicodeString + * @stable ICU 4.2 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_checkUnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &id, + int32_t *position, + UErrorCode *status); + +/** + * Check the specified string for possible security issues. + * The text to be checked will typically be an identifier of some sort. + * The set of checks to be performed is specified with uspoof_setChecks(). + * + * @param sc The USpoofChecker + * @param id A identifier to be checked for possible security issues. + * @param checkResult An instance of USpoofCheckResult to be filled with + * details about the identifier. Can be NULL. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Spoofing or security issues detected with the input string are + * not reported here, but through the function's return value. + * @return An integer value with bits set for any potential security + * or spoofing issues detected. The bits are defined by + * enum USpoofChecks. (returned_value & USPOOF_ALL_CHECKS) + * will be zero if the input string passes all of the + * enabled checks. Any information in this bitmask will be + * consistent with the information saved in the optional + * checkResult parameter. + * @see uspoof_openCheckResult + * @see uspoof_check2 + * @see uspoof_check2UTF8 + * @stable ICU 58 + */ +U_CAPI int32_t U_EXPORT2 +uspoof_check2UnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &id, + USpoofCheckResult* checkResult, + UErrorCode *status); + +/** + * A version of {@link uspoof_areConfusable} accepting UnicodeStrings. + * + * @param sc The USpoofChecker + * @param s1 The first of the two identifiers to be compared for + * confusability. The strings are in UTF-8 format. + * @param s2 The second of the two identifiers to be compared for + * confusability. The strings are in UTF-8 format. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * Confusability of the identifiers is not reported here, + * but through this function's return value. + * @return An integer value with bit(s) set corresponding to + * the type of confusability found, as defined by + * enum USpoofChecks. Zero is returned if the identifiers + * are not confusable. + * + * @stable ICU 4.2 + * + * @see uspoof_areConfusable + */ +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusableUnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &s1, + const icu::UnicodeString &s2, + UErrorCode *status); + +/** + * Get the "skeleton" for an identifier. + * Skeletons are a transformation of the input identifier; + * Two identifiers are confusable if their skeletons are identical. + * See Unicode UAX #39 for additional information. + * + * Using skeletons directly makes it possible to quickly check + * whether an identifier is confusable with any of some large + * set of existing identifiers, by creating an efficiently + * searchable collection of the skeletons. + * + * @param sc The USpoofChecker. + * @param type Deprecated in ICU 58. You may pass any number. + * Originally, controlled which of the Unicode confusable data + * tables to use. + * @param id The input identifier whose skeleton will be computed. + * @param dest The output identifier, to receive the skeleton string. + * @param status The error code, set if an error occurred while attempting to + * perform the check. + * @return A reference to the destination (skeleton) string. + * + * @stable ICU 4.2 + */ +U_I18N_API icu::UnicodeString & U_EXPORT2 +uspoof_getSkeletonUnicodeString(const USpoofChecker *sc, + uint32_t type, + const icu::UnicodeString &id, + icu::UnicodeString &dest, + UErrorCode *status); + +/** + * Get the set of Candidate Characters for Inclusion in Identifiers, as defined + * in http://unicode.org/Public/security/latest/xidmodifications.txt + * and documented in http://www.unicode.org/reports/tr39/, Unicode Security Mechanisms. + * + * The returned set is frozen. Ownership of the set remains with the ICU library; it must not + * be deleted by the caller. + * + * @param status The error code, set if a problem occurs while creating the set. + * + * @stable ICU 51 + */ +U_CAPI const icu::UnicodeSet * U_EXPORT2 +uspoof_getInclusionUnicodeSet(UErrorCode *status); + +/** + * Get the set of characters from Recommended Scripts for Inclusion in Identifiers, as defined + * in http://unicode.org/Public/security/latest/xidmodifications.txt + * and documented in http://www.unicode.org/reports/tr39/, Unicode Security Mechanisms. + * + * The returned set is frozen. Ownership of the set remains with the ICU library; it must not + * be deleted by the caller. + * + * @param status The error code, set if a problem occurs while creating the set. + * + * @stable ICU 51 + */ +U_CAPI const icu::UnicodeSet * U_EXPORT2 +uspoof_getRecommendedUnicodeSet(UErrorCode *status); + +#endif /* U_SHOW_CPLUSPLUS_API */ + +#endif /* UCONFIG_NO_NORMALIZATION */ + +#endif /* USPOOF_H */ diff --git a/thirdparty/icu4c/i18n/uspoof.cpp b/thirdparty/icu4c/i18n/uspoof.cpp new file mode 100644 index 0000000000..dd4618baa7 --- /dev/null +++ b/thirdparty/icu4c/i18n/uspoof.cpp @@ -0,0 +1,839 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 2008-2015, International Business Machines Corporation +* and others. All Rights Reserved. +*************************************************************************** +* file name: uspoof.cpp +* encoding: UTF-8 +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2008Feb13 +* created by: Andy Heninger +* +* Unicode Spoof Detection +*/ +#include "unicode/utypes.h" +#include "unicode/normalizer2.h" +#include "unicode/uspoof.h" +#include "unicode/ustring.h" +#include "unicode/utf16.h" +#include "cmemory.h" +#include "cstring.h" +#include "mutex.h" +#include "scriptset.h" +#include "uassert.h" +#include "ucln_in.h" +#include "uspoof_impl.h" +#include "umutex.h" + + +#if !UCONFIG_NO_NORMALIZATION + +U_NAMESPACE_USE + + +// +// Static Objects used by the spoof impl, their thread safe initialization and their cleanup. +// +static UnicodeSet *gInclusionSet = NULL; +static UnicodeSet *gRecommendedSet = NULL; +static const Normalizer2 *gNfdNormalizer = NULL; +static UInitOnce gSpoofInitStaticsOnce = U_INITONCE_INITIALIZER; + +namespace { + +UBool U_CALLCONV +uspoof_cleanup(void) { + delete gInclusionSet; + gInclusionSet = NULL; + delete gRecommendedSet; + gRecommendedSet = NULL; + gNfdNormalizer = NULL; + gSpoofInitStaticsOnce.reset(); + return TRUE; +} + +void U_CALLCONV initializeStatics(UErrorCode &status) { + static const char16_t *inclusionPat = + u"['\\-.\\:\\u00B7\\u0375\\u058A\\u05F3\\u05F4\\u06FD\\u06FE\\u0F0B\\u200C" + u"\\u200D\\u2010\\u2019\\u2027\\u30A0\\u30FB]"; + gInclusionSet = new UnicodeSet(UnicodeString(inclusionPat), status); + if (gInclusionSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + gInclusionSet->freeze(); + + // Note: data from IdentifierStatus.txt & IdentifierType.txt + // There is tooling to generate this constant in the unicodetools project: + // org.unicode.text.tools.RecommendedSetGenerator + // It will print the Java and C++ code to the console for easy copy-paste into this file. + static const char16_t *recommendedPat = + u"[0-9A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u0131\\u0134-\\u013E" + u"\\u0141-\\u0148\\u014A-\\u017E\\u018F\\u01A0\\u01A1\\u01AF\\u01B0\\u01CD-" + u"\\u01DC\\u01DE-\\u01E3\\u01E6-\\u01F0\\u01F4\\u01F5\\u01F8-\\u021B\\u021E" + u"\\u021F\\u0226-\\u0233\\u0259\\u02BB\\u02BC\\u02EC\\u0300-\\u0304\\u0306-" + u"\\u030C\\u030F-\\u0311\\u0313\\u0314\\u031B\\u0323-\\u0328\\u032D\\u032E" + u"\\u0330\\u0331\\u0335\\u0338\\u0339\\u0342\\u0345\\u037B-\\u037D\\u0386" + u"\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03CE\\u03FC-\\u045F\\u048A-" + u"\\u04FF\\u0510-\\u0529\\u052E\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0586" + u"\\u05B4\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u063F\\u0641-\\u0655\\u0660-" + u"\\u0669\\u0670-\\u0672\\u0674\\u0679-\\u068D\\u068F-\\u06A0\\u06A2-\\u06D3" + u"\\u06D5\\u06E5\\u06E6\\u06EE-\\u06FC\\u06FF\\u0750-\\u07B1\\u0870-\\u0887" + u"\\u0889-\\u088E\\u08A0-\\u08AC\\u08B2\\u08B5-\\u08C9\\u0901-\\u094D\\u094F" + u"\\u0950\\u0956\\u0957\\u0960-\\u0963\\u0966-\\u096F\\u0971-\\u0977\\u0979-" + u"\\u097F\\u0981-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-" + u"\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE" + u"\\u09D7\\u09E0-\\u09E3\\u09E6-\\u09F1\\u09FE\\u0A01-\\u0A03\\u0A05-\\u0A0A" + u"\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A35\\u0A38\\u0A39" + u"\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A5C\\u0A66-\\u0A74" + u"\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0" + u"\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD" + u"\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0AFA-\\u0AFF\\u0B01-\\u0B03\\u0B05-" + u"\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-" + u"\\u0B39\\u0B3C-\\u0B43\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B55-\\u0B57\\u0B5F-" + u"\\u0B61\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90" + u"\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-" + u"\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0" + u"\\u0BD7\\u0BE6-\\u0BEF\\u0C01-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-" + u"\\u0C33\\u0C35-\\u0C39\\u0C3C-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55" + u"\\u0C56\\u0C5D\\u0C60\\u0C61\\u0C66-\\u0C6F\\u0C80\\u0C82\\u0C83\\u0C85-" + u"\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-" + u"\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDD\\u0CE0-\\u0CE3" + u"\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D00\\u0D02\\u0D03\\u0D05-\\u0D0C\\u0D0E-" + u"\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D43\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D54-" + u"\\u0D57\\u0D60\\u0D61\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-" + u"\\u0D8E\\u0D91-\\u0D96\\u0D9A-\\u0DA5\\u0DA7-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD" + u"\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDE\\u0DF2\\u0E01-" + u"\\u0E32\\u0E34-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84" + u"\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB2\\u0EB4-\\u0EBD\\u0EC0-" + u"\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDE\\u0EDF\\u0F00\\u0F20-" + u"\\u0F29\\u0F35\\u0F37\\u0F3E-\\u0F42\\u0F44-\\u0F47\\u0F49-\\u0F4C\\u0F4E-" + u"\\u0F51\\u0F53-\\u0F56\\u0F58-\\u0F5B\\u0F5D-\\u0F68\\u0F6A-\\u0F6C\\u0F71" + u"\\u0F72\\u0F74\\u0F7A-\\u0F80\\u0F82-\\u0F84\\u0F86-\\u0F92\\u0F94-\\u0F97" + u"\\u0F99-\\u0F9C\\u0F9E-\\u0FA1\\u0FA3-\\u0FA6\\u0FA8-\\u0FAB\\u0FAD-\\u0FB8" + u"\\u0FBA-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10C7\\u10CD\\u10D0-" + u"\\u10F0\\u10F7-\\u10FA\\u10FD-\\u10FF\\u1200-\\u1248\\u124A-\\u124D\\u1250-" + u"\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0" + u"\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-" + u"\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u1780-" + u"\\u17A2\\u17A5-\\u17A7\\u17A9-\\u17B3\\u17B6-\\u17CD\\u17D0\\u17D2\\u17D7" + u"\\u17DC\\u17E0-\\u17E9\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1E00-\\u1E99\\u1E9E" + u"\\u1EA0-\\u1EF9\\u1F00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D" + u"\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F70\\u1F72\\u1F74\\u1F76" + u"\\u1F78\\u1F7A\\u1F7C\\u1F80-\\u1FB4\\u1FB6-\\u1FBA\\u1FBC\\u1FC2-\\u1FC4" + u"\\u1FC6-\\u1FC8\\u1FCA\\u1FCC\\u1FD0-\\u1FD2\\u1FD6-\\u1FDA\\u1FE0-\\u1FE2" + u"\\u1FE4-\\u1FEA\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FF8\\u1FFA\\u1FFC\\u2D27" + u"\\u2D2D\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-" + u"\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u3005-" + u"\\u3007\\u3041-\\u3096\\u3099\\u309A\\u309D\\u309E\\u30A1-\\u30FA\\u30FC-" + u"\\u30FE\\u3105-\\u312D\\u312F\\u31A0-\\u31BF\\u3400-\\u4DBF\\u4E00-\\u9FFF" + u"\\uA67F\\uA717-\\uA71F\\uA788\\uA78D\\uA792\\uA793\\uA7AA\\uA7AE\\uA7B8" + u"\\uA7B9\\uA7C0-\\uA7CA\\uA7D0\\uA7D1\\uA7D3\\uA7D5-\\uA7D9\\uA9E7-\\uA9FE" + u"\\uAA60-\\uAA76\\uAA7A-\\uAA7F\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16" + u"\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB66\\uAB67\\uAC00-\\uD7A3\\uFA0E\\uFA0F" + u"\\uFA11\\uFA13\\uFA14\\uFA1F\\uFA21\\uFA23\\uFA24\\uFA27-\\uFA29\\U00011301" + u"\\U00011303\\U0001133B\\U0001133C\\U00016FF0\\U00016FF1\\U0001B11F-" + u"\\U0001B122\\U0001B150-\\U0001B152\\U0001B164-\\U0001B167\\U0001DF00-" + u"\\U0001DF1E\\U0001E7E0-\\U0001E7E6\\U0001E7E8-\\U0001E7EB\\U0001E7ED" + u"\\U0001E7EE\\U0001E7F0-\\U0001E7FE\\U00020000-\\U0002A6DF\\U0002A700-" + u"\\U0002B738\\U0002B740-\\U0002B81D\\U0002B820-\\U0002CEA1\\U0002CEB0-" + u"\\U0002EBE0\\U00030000-\\U0003134A]"; + + gRecommendedSet = new UnicodeSet(UnicodeString(recommendedPat), status); + if (gRecommendedSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete gInclusionSet; + return; + } + gRecommendedSet->freeze(); + gNfdNormalizer = Normalizer2::getNFDInstance(status); + ucln_i18n_registerCleanup(UCLN_I18N_SPOOF, uspoof_cleanup); +} + +} // namespace + +U_CFUNC void uspoof_internalInitStatics(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); +} + +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_open(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + if (U_FAILURE(*status)) { + return NULL; + } + SpoofImpl *si = new SpoofImpl(*status); + if (si == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + if (U_FAILURE(*status)) { + delete si; + return NULL; + } + return si->asUSpoofChecker(); +} + + +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_openFromSerialized(const void *data, int32_t length, int32_t *pActualLength, + UErrorCode *status) { + if (U_FAILURE(*status)) { + return NULL; + } + + if (data == NULL) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + if (U_FAILURE(*status)) + { + return NULL; + } + + SpoofData *sd = new SpoofData(data, length, *status); + if (sd == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + if (U_FAILURE(*status)) { + delete sd; + return NULL; + } + + SpoofImpl *si = new SpoofImpl(sd, *status); + if (si == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + delete sd; // explicit delete as the destructor for si won't be called. + return NULL; + } + + if (U_FAILURE(*status)) { + delete si; // no delete for sd, as the si destructor will delete it. + return NULL; + } + + if (pActualLength != NULL) { + *pActualLength = sd->size(); + } + return si->asUSpoofChecker(); +} + + +U_CAPI USpoofChecker * U_EXPORT2 +uspoof_clone(const USpoofChecker *sc, UErrorCode *status) { + const SpoofImpl *src = SpoofImpl::validateThis(sc, *status); + if (src == NULL) { + return NULL; + } + SpoofImpl *result = new SpoofImpl(*src, *status); // copy constructor + if (result == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + if (U_FAILURE(*status)) { + delete result; + result = NULL; + } + return result->asUSpoofChecker(); +} + + +U_CAPI void U_EXPORT2 +uspoof_close(USpoofChecker *sc) { + UErrorCode status = U_ZERO_ERROR; + SpoofImpl *This = SpoofImpl::validateThis(sc, status); + delete This; +} + + +U_CAPI void U_EXPORT2 +uspoof_setChecks(USpoofChecker *sc, int32_t checks, UErrorCode *status) { + SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return; + } + + // Verify that the requested checks are all ones (bits) that + // are acceptable, known values. + if (checks & ~(USPOOF_ALL_CHECKS | USPOOF_AUX_INFO)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + This->fChecks = checks; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_getChecks(const USpoofChecker *sc, UErrorCode *status) { + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return 0; + } + return This->fChecks; +} + +U_CAPI void U_EXPORT2 +uspoof_setRestrictionLevel(USpoofChecker *sc, URestrictionLevel restrictionLevel) { + UErrorCode status = U_ZERO_ERROR; + SpoofImpl *This = SpoofImpl::validateThis(sc, status); + if (This != NULL) { + This->fRestrictionLevel = restrictionLevel; + This->fChecks |= USPOOF_RESTRICTION_LEVEL; + } +} + +U_CAPI URestrictionLevel U_EXPORT2 +uspoof_getRestrictionLevel(const USpoofChecker *sc) { + UErrorCode status = U_ZERO_ERROR; + const SpoofImpl *This = SpoofImpl::validateThis(sc, status); + if (This == NULL) { + return USPOOF_UNRESTRICTIVE; + } + return This->fRestrictionLevel; +} + +U_CAPI void U_EXPORT2 +uspoof_setAllowedLocales(USpoofChecker *sc, const char *localesList, UErrorCode *status) { + SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return; + } + This->setAllowedLocales(localesList, *status); +} + +U_CAPI const char * U_EXPORT2 +uspoof_getAllowedLocales(USpoofChecker *sc, UErrorCode *status) { + SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return NULL; + } + return This->getAllowedLocales(*status); +} + + +U_CAPI const USet * U_EXPORT2 +uspoof_getAllowedChars(const USpoofChecker *sc, UErrorCode *status) { + const UnicodeSet *result = uspoof_getAllowedUnicodeSet(sc, status); + return result->toUSet(); +} + +U_CAPI const UnicodeSet * U_EXPORT2 +uspoof_getAllowedUnicodeSet(const USpoofChecker *sc, UErrorCode *status) { + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return NULL; + } + return This->fAllowedCharsSet; +} + + +U_CAPI void U_EXPORT2 +uspoof_setAllowedChars(USpoofChecker *sc, const USet *chars, UErrorCode *status) { + const UnicodeSet *set = UnicodeSet::fromUSet(chars); + uspoof_setAllowedUnicodeSet(sc, set, status); +} + + +U_CAPI void U_EXPORT2 +uspoof_setAllowedUnicodeSet(USpoofChecker *sc, const UnicodeSet *chars, UErrorCode *status) { + SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return; + } + if (chars->isBogus()) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + UnicodeSet *clonedSet = chars->clone(); + if (clonedSet == NULL || clonedSet->isBogus()) { + *status = U_MEMORY_ALLOCATION_ERROR; + return; + } + clonedSet->freeze(); + delete This->fAllowedCharsSet; + This->fAllowedCharsSet = clonedSet; + This->fChecks |= USPOOF_CHAR_LIMIT; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_check(const USpoofChecker *sc, + const UChar *id, int32_t length, + int32_t *position, + UErrorCode *status) { + + // Backwards compatibility: + if (position != NULL) { + *position = 0; + } + + // Delegate to uspoof_check2 + return uspoof_check2(sc, id, length, NULL, status); +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_check2(const USpoofChecker *sc, + const UChar* id, int32_t length, + USpoofCheckResult* checkResult, + UErrorCode *status) { + + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return 0; + } + if (length < -1) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString idStr((length == -1), id, length); // Aliasing constructor. + int32_t result = uspoof_check2UnicodeString(sc, idStr, checkResult, status); + return result; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_checkUTF8(const USpoofChecker *sc, + const char *id, int32_t length, + int32_t *position, + UErrorCode *status) { + + // Backwards compatibility: + if (position != NULL) { + *position = 0; + } + + // Delegate to uspoof_check2 + return uspoof_check2UTF8(sc, id, length, NULL, status); +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_check2UTF8(const USpoofChecker *sc, + const char *id, int32_t length, + USpoofCheckResult* checkResult, + UErrorCode *status) { + + if (U_FAILURE(*status)) { + return 0; + } + UnicodeString idStr = UnicodeString::fromUTF8(StringPiece(id, length>=0 ? length : static_cast(uprv_strlen(id)))); + int32_t result = uspoof_check2UnicodeString(sc, idStr, checkResult, status); + return result; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusable(const USpoofChecker *sc, + const UChar *id1, int32_t length1, + const UChar *id2, int32_t length2, + UErrorCode *status) { + SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return 0; + } + if (length1 < -1 || length2 < -1) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UnicodeString id1Str((length1==-1), id1, length1); // Aliasing constructor + UnicodeString id2Str((length2==-1), id2, length2); // Aliasing constructor + return uspoof_areConfusableUnicodeString(sc, id1Str, id2Str, status); +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusableUTF8(const USpoofChecker *sc, + const char *id1, int32_t length1, + const char *id2, int32_t length2, + UErrorCode *status) { + SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return 0; + } + if (length1 < -1 || length2 < -1) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString id1Str = UnicodeString::fromUTF8(StringPiece(id1, length1>=0? length1 : static_cast(uprv_strlen(id1)))); + UnicodeString id2Str = UnicodeString::fromUTF8(StringPiece(id2, length2>=0? length2 : static_cast(uprv_strlen(id2)))); + int32_t results = uspoof_areConfusableUnicodeString(sc, id1Str, id2Str, status); + return results; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_areConfusableUnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &id1, + const icu::UnicodeString &id2, + UErrorCode *status) { + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return 0; + } + // + // See section 4 of UAX 39 for the algorithm for checking whether two strings are confusable, + // and for definitions of the types (single, whole, mixed-script) of confusables. + + // We only care about a few of the check flags. Ignore the others. + // If no tests relevant to this function have been specified, return an error. + // TODO: is this really the right thing to do? It's probably an error on the caller's part, + // but logically we would just return 0 (no error). + if ((This->fChecks & USPOOF_CONFUSABLE) == 0) { + *status = U_INVALID_STATE_ERROR; + return 0; + } + + // Compute the skeletons and check for confusability. + UnicodeString id1Skeleton; + uspoof_getSkeletonUnicodeString(sc, 0 /* deprecated */, id1, id1Skeleton, status); + UnicodeString id2Skeleton; + uspoof_getSkeletonUnicodeString(sc, 0 /* deprecated */, id2, id2Skeleton, status); + if (U_FAILURE(*status)) { return 0; } + if (id1Skeleton != id2Skeleton) { + return 0; + } + + // If we get here, the strings are confusable. Now we just need to set the flags for the appropriate classes + // of confusables according to UTS 39 section 4. + // Start by computing the resolved script sets of id1 and id2. + ScriptSet id1RSS; + This->getResolvedScriptSet(id1, id1RSS, *status); + ScriptSet id2RSS; + This->getResolvedScriptSet(id2, id2RSS, *status); + + // Turn on all applicable flags + int32_t result = 0; + if (id1RSS.intersects(id2RSS)) { + result |= USPOOF_SINGLE_SCRIPT_CONFUSABLE; + } else { + result |= USPOOF_MIXED_SCRIPT_CONFUSABLE; + if (!id1RSS.isEmpty() && !id2RSS.isEmpty()) { + result |= USPOOF_WHOLE_SCRIPT_CONFUSABLE; + } + } + + // Turn off flags that the user doesn't want + if ((This->fChecks & USPOOF_SINGLE_SCRIPT_CONFUSABLE) == 0) { + result &= ~USPOOF_SINGLE_SCRIPT_CONFUSABLE; + } + if ((This->fChecks & USPOOF_MIXED_SCRIPT_CONFUSABLE) == 0) { + result &= ~USPOOF_MIXED_SCRIPT_CONFUSABLE; + } + if ((This->fChecks & USPOOF_WHOLE_SCRIPT_CONFUSABLE) == 0) { + result &= ~USPOOF_WHOLE_SCRIPT_CONFUSABLE; + } + + return result; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_checkUnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &id, + int32_t *position, + UErrorCode *status) { + + // Backwards compatibility: + if (position != NULL) { + *position = 0; + } + + // Delegate to uspoof_check2 + return uspoof_check2UnicodeString(sc, id, NULL, status); +} + +namespace { + +int32_t checkImpl(const SpoofImpl* This, const UnicodeString& id, CheckResult* checkResult, UErrorCode* status) { + U_ASSERT(This != NULL); + U_ASSERT(checkResult != NULL); + checkResult->clear(); + int32_t result = 0; + + if (0 != (This->fChecks & USPOOF_RESTRICTION_LEVEL)) { + URestrictionLevel idRestrictionLevel = This->getRestrictionLevel(id, *status); + if (idRestrictionLevel > This->fRestrictionLevel) { + result |= USPOOF_RESTRICTION_LEVEL; + } + checkResult->fRestrictionLevel = idRestrictionLevel; + } + + if (0 != (This->fChecks & USPOOF_MIXED_NUMBERS)) { + UnicodeSet numerics; + This->getNumerics(id, numerics, *status); + if (numerics.size() > 1) { + result |= USPOOF_MIXED_NUMBERS; + } + checkResult->fNumerics = numerics; // UnicodeSet::operator= + } + + if (0 != (This->fChecks & USPOOF_HIDDEN_OVERLAY)) { + int32_t index = This->findHiddenOverlay(id, *status); + if (index != -1) { + result |= USPOOF_HIDDEN_OVERLAY; + } + } + + + if (0 != (This->fChecks & USPOOF_CHAR_LIMIT)) { + int32_t i; + UChar32 c; + int32_t length = id.length(); + for (i=0; ifAllowedCharsSet->contains(c)) { + result |= USPOOF_CHAR_LIMIT; + break; + } + } + } + + if (0 != (This->fChecks & USPOOF_INVISIBLE)) { + // This check needs to be done on NFD input + UnicodeString nfdText; + gNfdNormalizer->normalize(id, nfdText, *status); + int32_t nfdLength = nfdText.length(); + + // scan for more than one occurrence of the same non-spacing mark + // in a sequence of non-spacing marks. + int32_t i; + UChar32 c; + UChar32 firstNonspacingMark = 0; + UBool haveMultipleMarks = FALSE; + UnicodeSet marksSeenSoFar; // Set of combining marks in a single combining sequence. + + for (i=0; ifChecks = result; + return checkResult->toCombinedBitmask(This->fChecks); +} + +} // namespace + +U_CAPI int32_t U_EXPORT2 +uspoof_check2UnicodeString(const USpoofChecker *sc, + const icu::UnicodeString &id, + USpoofCheckResult* checkResult, + UErrorCode *status) { + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + return FALSE; + } + + if (checkResult != NULL) { + CheckResult* ThisCheckResult = CheckResult::validateThis(checkResult, *status); + if (ThisCheckResult == NULL) { + return FALSE; + } + return checkImpl(This, id, ThisCheckResult, status); + } else { + // Stack-allocate the checkResult since this method doesn't return it + CheckResult stackCheckResult; + return checkImpl(This, id, &stackCheckResult, status); + } +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_getSkeleton(const USpoofChecker *sc, + uint32_t type, + const UChar *id, int32_t length, + UChar *dest, int32_t destCapacity, + UErrorCode *status) { + + SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return 0; + } + if (length<-1 || destCapacity<0 || (destCapacity==0 && dest!=NULL)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UnicodeString idStr((length==-1), id, length); // Aliasing constructor + UnicodeString destStr; + uspoof_getSkeletonUnicodeString(sc, type, idStr, destStr, status); + destStr.extract(dest, destCapacity, *status); + return destStr.length(); +} + + + +U_I18N_API UnicodeString & U_EXPORT2 +uspoof_getSkeletonUnicodeString(const USpoofChecker *sc, + uint32_t /*type*/, + const UnicodeString &id, + UnicodeString &dest, + UErrorCode *status) { + const SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return dest; + } + + UnicodeString nfdId; + gNfdNormalizer->normalize(id, nfdId, *status); + + // Apply the skeleton mapping to the NFD normalized input string + // Accumulate the skeleton, possibly unnormalized, in a UnicodeString. + int32_t inputIndex = 0; + UnicodeString skelStr; + int32_t normalizedLen = nfdId.length(); + for (inputIndex=0; inputIndex < normalizedLen; ) { + UChar32 c = nfdId.char32At(inputIndex); + inputIndex += U16_LENGTH(c); + This->fSpoofData->confusableLookup(c, skelStr); + } + + gNfdNormalizer->normalize(skelStr, dest, *status); + return dest; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_getSkeletonUTF8(const USpoofChecker *sc, + uint32_t type, + const char *id, int32_t length, + char *dest, int32_t destCapacity, + UErrorCode *status) { + SpoofImpl::validateThis(sc, *status); + if (U_FAILURE(*status)) { + return 0; + } + if (length<-1 || destCapacity<0 || (destCapacity==0 && dest!=NULL)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + UnicodeString srcStr = UnicodeString::fromUTF8(StringPiece(id, length>=0 ? length : static_cast(uprv_strlen(id)))); + UnicodeString destStr; + uspoof_getSkeletonUnicodeString(sc, type, srcStr, destStr, status); + if (U_FAILURE(*status)) { + return 0; + } + + int32_t lengthInUTF8 = 0; + u_strToUTF8(dest, destCapacity, &lengthInUTF8, + destStr.getBuffer(), destStr.length(), status); + return lengthInUTF8; +} + + +U_CAPI int32_t U_EXPORT2 +uspoof_serialize(USpoofChecker *sc,void *buf, int32_t capacity, UErrorCode *status) { + SpoofImpl *This = SpoofImpl::validateThis(sc, *status); + if (This == NULL) { + U_ASSERT(U_FAILURE(*status)); + return 0; + } + + return This->fSpoofData->serialize(buf, capacity, *status); +} + +U_CAPI const USet * U_EXPORT2 +uspoof_getInclusionSet(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + return gInclusionSet->toUSet(); +} + +U_CAPI const USet * U_EXPORT2 +uspoof_getRecommendedSet(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + return gRecommendedSet->toUSet(); +} + +U_I18N_API const UnicodeSet * U_EXPORT2 +uspoof_getInclusionUnicodeSet(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + return gInclusionSet; +} + +U_I18N_API const UnicodeSet * U_EXPORT2 +uspoof_getRecommendedUnicodeSet(UErrorCode *status) { + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + return gRecommendedSet; +} + +//------------------ +// CheckResult APIs +//------------------ + +U_CAPI USpoofCheckResult* U_EXPORT2 +uspoof_openCheckResult(UErrorCode *status) { + CheckResult* checkResult = new CheckResult(); + if (checkResult == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + return checkResult->asUSpoofCheckResult(); +} + +U_CAPI void U_EXPORT2 +uspoof_closeCheckResult(USpoofCheckResult* checkResult) { + UErrorCode status = U_ZERO_ERROR; + CheckResult* This = CheckResult::validateThis(checkResult, status); + delete This; +} + +U_CAPI int32_t U_EXPORT2 +uspoof_getCheckResultChecks(const USpoofCheckResult *checkResult, UErrorCode *status) { + const CheckResult* This = CheckResult::validateThis(checkResult, *status); + if (U_FAILURE(*status)) { return 0; } + return This->fChecks; +} + +U_CAPI URestrictionLevel U_EXPORT2 +uspoof_getCheckResultRestrictionLevel(const USpoofCheckResult *checkResult, UErrorCode *status) { + const CheckResult* This = CheckResult::validateThis(checkResult, *status); + if (U_FAILURE(*status)) { return USPOOF_UNRESTRICTIVE; } + return This->fRestrictionLevel; +} + +U_CAPI const USet* U_EXPORT2 +uspoof_getCheckResultNumerics(const USpoofCheckResult *checkResult, UErrorCode *status) { + const CheckResult* This = CheckResult::validateThis(checkResult, *status); + if (U_FAILURE(*status)) { return NULL; } + return This->fNumerics.toUSet(); +} + + + +#endif // !UCONFIG_NO_NORMALIZATION diff --git a/thirdparty/icu4c/i18n/uspoof_impl.cpp b/thirdparty/icu4c/i18n/uspoof_impl.cpp new file mode 100644 index 0000000000..b283d81321 --- /dev/null +++ b/thirdparty/icu4c/i18n/uspoof_impl.cpp @@ -0,0 +1,959 @@ +// © 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. +********************************************************************** +*/ + +#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::validateThis(const_cast(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; iaddAll(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 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; iconfusableLookup(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; i9) { + 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(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( + const_cast(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(data); + fRawData = static_cast(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(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_EXIT; + } + + numBytes = (numBytes + 15) & ~15; // Round up to a multiple of 16 + uint32_t returnOffset = fMemLimit; + fMemLimit += numBytes; + fRawData = static_cast(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 + + diff --git a/thirdparty/icu4c/i18n/uspoof_impl.h b/thirdparty/icu4c/i18n/uspoof_impl.h new file mode 100644 index 0000000000..e75ae262bd --- /dev/null +++ b/thirdparty/icu4c/i18n/uspoof_impl.h @@ -0,0 +1,343 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +*************************************************************************** +* Copyright (C) 2008-2013, International Business Machines Corporation +* and others. All Rights Reserved. +*************************************************************************** +* +* uspoof_impl.h +* +* Implementation header for spoof detection +* +*/ + +#ifndef USPOOFIM_H +#define USPOOFIM_H + +#include "uassert.h" +#include "unicode/utypes.h" +#include "unicode/uspoof.h" +#include "unicode/uscript.h" +#include "unicode/udata.h" +#include "udataswp.h" +#include "utrie2.h" + +#if !UCONFIG_NO_NORMALIZATION + +#ifdef __cplusplus + +#include "capi_helper.h" + +U_NAMESPACE_BEGIN + +// The maximum length (in UTF-16 UChars) of the skeleton replacement string resulting from +// a single input code point. This is function of the unicode.org data. +#define USPOOF_MAX_SKELETON_EXPANSION 20 + +// The default stack buffer size for copies or conversions or normalizations +// of input strings being checked. (Used in multiple places.) +#define USPOOF_STACK_BUFFER_SIZE 100 + +// Magic number for sanity checking spoof data. +#define USPOOF_MAGIC 0x3845fdef + +// Magic number for sanity checking spoof checkers. +#define USPOOF_CHECK_MAGIC 0x2734ecde + +class ScriptSet; +class SpoofData; +struct SpoofDataHeader; +class ConfusableDataUtils; + +/** + * Class SpoofImpl corresponds directly to the plain C API opaque type + * USpoofChecker. One can be cast to the other. + */ +class SpoofImpl : public UObject, + public IcuCApiHelper { +public: + SpoofImpl(SpoofData *data, UErrorCode& status); + SpoofImpl(UErrorCode& status); + SpoofImpl(); + void construct(UErrorCode& status); + virtual ~SpoofImpl(); + + /** Copy constructor, used by the user level uspoof_clone() function. + */ + SpoofImpl(const SpoofImpl &src, UErrorCode &status); + + USpoofChecker *asUSpoofChecker(); + static SpoofImpl *validateThis(USpoofChecker *sc, UErrorCode &status); + static const SpoofImpl *validateThis(const USpoofChecker *sc, UErrorCode &status); + + /** Set and Get AllowedLocales, implementations of the corresponding API */ + void setAllowedLocales(const char *localesList, UErrorCode &status); + const char * getAllowedLocales(UErrorCode &status); + + // Add (union) to the UnicodeSet all of the characters for the scripts used for + // the specified locale. Part of the implementation of setAllowedLocales. + void addScriptChars(const char *locale, UnicodeSet *allowedChars, UErrorCode &status); + + // Functions implementing the features of UTS 39 section 5. + static void getAugmentedScriptSet(UChar32 codePoint, ScriptSet& result, UErrorCode& status); + void getResolvedScriptSet(const UnicodeString& input, ScriptSet& result, UErrorCode& status) const; + void getResolvedScriptSetWithout(const UnicodeString& input, UScriptCode script, ScriptSet& result, UErrorCode& status) const; + void getNumerics(const UnicodeString& input, UnicodeSet& result, UErrorCode& status) const; + URestrictionLevel getRestrictionLevel(const UnicodeString& input, UErrorCode& status) const; + + int32_t findHiddenOverlay(const UnicodeString& input, UErrorCode& status) const; + bool isIllegalCombiningDotLeadCharacter(UChar32 cp) const; + + /** parse a hex number. Untility used by the builders. */ + static UChar32 ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status); + + static UClassID U_EXPORT2 getStaticClassID(void); + virtual UClassID getDynamicClassID(void) const override; + + // + // Data Members + // + + int32_t fChecks; // Bit vector of checks to perform. + + SpoofData *fSpoofData; + + const UnicodeSet *fAllowedCharsSet; // The UnicodeSet of allowed characters. + // for this Spoof Checker. Defaults to all chars. + + const char *fAllowedLocales; // The list of allowed locales. + URestrictionLevel fRestrictionLevel; // The maximum restriction level for an acceptable identifier. +}; + +/** + * Class CheckResult corresponds directly to the plain C API opaque type + * USpoofCheckResult. One can be cast to the other. + */ +class CheckResult : public UObject, + public IcuCApiHelper { +public: + CheckResult(); + virtual ~CheckResult(); + + USpoofCheckResult *asUSpoofCheckResult(); + static CheckResult *validateThis(USpoofCheckResult *ptr, UErrorCode &status); + static const CheckResult *validateThis(const USpoofCheckResult *ptr, UErrorCode &status); + + void clear(); + + // Used to convert this CheckResult to the older int32_t return value API + int32_t toCombinedBitmask(int32_t expectedChecks); + + // Data Members + int32_t fChecks; // Bit vector of checks that were failed. + UnicodeSet fNumerics; // Set of numerics found in the string. + URestrictionLevel fRestrictionLevel; // The restriction level of the string. +}; + + +// +// Confusable Mappings Data Structures, version 2.0 +// +// For the confusable data, we are essentially implementing a map, +// key: a code point +// value: a string. Most commonly one char in length, but can be more. +// +// The keys are stored as a sorted array of 32 bit ints. +// bits 0-23 a code point value +// bits 24-31 length of value string, in UChars (between 1 and 256 UChars). +// The key table is sorted in ascending code point order. (not on the +// 32 bit int value, the flag bits do not participate in the sorting.) +// +// Lookup is done by means of a binary search in the key table. +// +// The corresponding values are kept in a parallel array of 16 bit ints. +// If the value string is of length 1, it is literally in the value array. +// For longer strings, the value array contains an index into the strings table. +// +// String Table: +// The strings table contains all of the value strings (those of length two or greater) +// concatenated together into one long UChar (UTF-16) array. +// +// There is no nul character or other mark between adjacent strings. +// +//---------------------------------------------------------------------------- +// +// Changes from format version 1 to format version 2: +// 1) Removal of the whole-script confusable data tables. +// 2) Removal of the SL/SA/ML/MA and multi-table flags in the key bitmask. +// 3) Expansion of string length value in the key bitmask from 2 bits to 8 bits. +// 4) Removal of the string lengths table since 8 bits is sufficient for the +// lengths of all entries in confusables.txt. + + + +// Internal functions for manipulating confusable data table keys +#define USPOOF_CONFUSABLE_DATA_FORMAT_VERSION 2 // version for ICU 58 +class ConfusableDataUtils { +public: + inline static UChar32 keyToCodePoint(int32_t key) { + return key & 0x00ffffff; + } + inline static int32_t keyToLength(int32_t key) { + return ((key & 0xff000000) >> 24) + 1; + } + inline static int32_t codePointAndLengthToKey(UChar32 codePoint, int32_t length) { + U_ASSERT((codePoint & 0x00ffffff) == codePoint); + U_ASSERT(length <= 256); + return codePoint | ((length - 1) << 24); + } +}; + + +//------------------------------------------------------------------------------------- +// +// SpoofData +// +// A small class that wraps the raw (usually memory mapped) spoof data. +// Serves two primary functions: +// 1. Convenience. Contains real pointers to the data, to avoid dealing with +// the offsets in the raw data. +// 2. Reference counting. When a spoof checker is cloned, the raw data is shared +// and must be retained until all checkers using the data are closed. +// Nothing in this struct includes state that is specific to any particular +// USpoofDetector object. +// +//--------------------------------------------------------------------------------------- +class SpoofData: public UMemory { + public: + static SpoofData* getDefault(UErrorCode &status); // Get standard ICU spoof data. + static void releaseDefault(); // Cleanup reference to default spoof data. + + SpoofData(UErrorCode &status); // Create new spoof data wrapper. + // Only used when building new data from rules. + + // Constructor for use when creating from prebuilt default data. + // A UDataMemory is what the ICU internal data loading functions provide. + // The udm is adopted by the SpoofData. + SpoofData(UDataMemory *udm, UErrorCode &status); + + // Constructor for use when creating from serialized data. + // + SpoofData(const void *serializedData, int32_t length, UErrorCode &status); + + // Check raw Spoof Data Version compatibility. + // Return true it looks good. + UBool validateDataVersion(UErrorCode &status) const; + + ~SpoofData(); // Destructor not normally used. + // Use removeReference() instead. + // Reference Counting functions. + // Clone of a user-level spoof detector increments the ref count on the data. + // Close of a user-level spoof detector decrements the ref count. + // If the data is owned by us, it will be deleted when count goes to zero. + SpoofData *addReference(); + void removeReference(); + + // Reset all fields to an initial state. + // Called from the top of all constructors. + void reset(); + + // Copy this instance's raw data buffer to the specified address. + int32_t serialize(void *buf, int32_t capacity, UErrorCode &status) const; + + // Get the total number of bytes of data backed by this SpoofData. + // Not to be confused with length, which returns the number of confusable entries. + int32_t size() const; + + // Get the confusable skeleton transform for a single code point. + // The result is a string with a length between 1 and 18 as of Unicode 9. + // This is the main public endpoint for this class. + // @return The length in UTF-16 code units of the substitution string. + int32_t confusableLookup(UChar32 inChar, UnicodeString &dest) const; + + // Get the number of confusable entries in this SpoofData. + int32_t length() const; + + // Get the code point (key) at the specified index. + UChar32 codePointAt(int32_t index) const; + + // Get the confusable skeleton (value) at the specified index. + // Append it to the specified UnicodeString&. + // @return The length in UTF-16 code units of the skeleton string. + int32_t appendValueTo(int32_t index, UnicodeString& dest) const; + + private: + // Reserve space in the raw data. For use by builder when putting together a + // new set of data. Init the new storage to zero, to prevent inconsistent + // results if it is not all otherwise set by the requester. + // Return: + // pointer to the new space that was added by this function. + void *reserveSpace(int32_t numBytes, UErrorCode &status); + + // initialize the pointers from this object to the raw data. + void initPtrs(UErrorCode &status); + + SpoofDataHeader *fRawData; // Ptr to the raw memory-mapped data + UBool fDataOwned; // True if the raw data is owned, and needs + // to be deleted when refcount goes to zero. + UDataMemory *fUDM; // If not NULL, our data came from a + // UDataMemory, which we must close when + // we are done. + + uint32_t fMemLimit; // Limit of available raw data space + u_atomic_int32_t fRefCount; + + // Confusable data + int32_t *fCFUKeys; + uint16_t *fCFUValues; + UChar *fCFUStrings; + + friend class ConfusabledataBuilder; +}; + +//--------------------------------------------------------------------------------------- +// +// Raw Binary Data Formats, as loaded from the ICU data file, +// or as built by the builder. +// +//--------------------------------------------------------------------------------------- +struct SpoofDataHeader { + int32_t fMagic; // (0x3845fdef) + uint8_t fFormatVersion[4]; // Data Format. Same as the value in struct UDataInfo + // if there is one associated with this data. + int32_t fLength; // Total length in bytes of this spoof data, + // including all sections, not just the header. + + // The following four sections refer to data representing the confusable data + // from the Unicode.org data from "confusables.txt" + + int32_t fCFUKeys; // byte offset to Keys table (from SpoofDataHeader *) + int32_t fCFUKeysSize; // number of entries in keys table (32 bits each) + + // TODO: change name to fCFUValues, for consistency. + int32_t fCFUStringIndex; // byte offset to String Indexes table + int32_t fCFUStringIndexSize; // number of entries in String Indexes table (16 bits each) + // (number of entries must be same as in Keys table + + int32_t fCFUStringTable; // byte offset of String table + int32_t fCFUStringTableLen; // length of string table (in 16 bit UChars) + + // The following sections are for data from xidmodifications.txt + + int32_t unused[15]; // Padding, Room for Expansion +}; + + + +U_NAMESPACE_END +#endif /* __cplusplus */ + +/** + * Endianness swap function for binary spoof data. + * @internal + */ +U_CAPI int32_t U_EXPORT2 +uspoof_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outData, + UErrorCode *status); + + +#endif + +#endif /* USPOOFIM_H */ + diff --git a/thirdparty/icu4c/icudt71l.dat b/thirdparty/icu4c/icudt71l.dat index 4c2c1c4d16c62eca0da1e2e6c1769e021d2f9ac8..3fa3af9c2315d6b5df2c70487ca38ade6e188030 100644 GIT binary patch delta 46587 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 Yrv1Q0+x7z&?b;7qwBLTh($