intl/icu/source/i18n/uspoof.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /*
michael@0 2 ***************************************************************************
michael@0 3 * Copyright (C) 2008-2013, International Business Machines Corporation
michael@0 4 * and others. All Rights Reserved.
michael@0 5 ***************************************************************************
michael@0 6 * file name: uspoof.cpp
michael@0 7 * encoding: US-ASCII
michael@0 8 * tab size: 8 (not used)
michael@0 9 * indentation:4
michael@0 10 *
michael@0 11 * created on: 2008Feb13
michael@0 12 * created by: Andy Heninger
michael@0 13 *
michael@0 14 * Unicode Spoof Detection
michael@0 15 */
michael@0 16 #include "unicode/utypes.h"
michael@0 17 #include "unicode/normalizer2.h"
michael@0 18 #include "unicode/uspoof.h"
michael@0 19 #include "unicode/ustring.h"
michael@0 20 #include "unicode/utf16.h"
michael@0 21 #include "cmemory.h"
michael@0 22 #include "cstring.h"
michael@0 23 #include "identifier_info.h"
michael@0 24 #include "mutex.h"
michael@0 25 #include "scriptset.h"
michael@0 26 #include "uassert.h"
michael@0 27 #include "ucln_in.h"
michael@0 28 #include "uspoof_impl.h"
michael@0 29 #include "umutex.h"
michael@0 30
michael@0 31
michael@0 32 #if !UCONFIG_NO_NORMALIZATION
michael@0 33
michael@0 34 U_NAMESPACE_USE
michael@0 35
michael@0 36
michael@0 37 //
michael@0 38 // Static Objects used by the spoof impl, their thread safe initialization and their cleanup.
michael@0 39 //
michael@0 40 static UnicodeSet *gInclusionSet = NULL;
michael@0 41 static UnicodeSet *gRecommendedSet = NULL;
michael@0 42 static const Normalizer2 *gNfdNormalizer = NULL;
michael@0 43 static UMutex gInitMutex = U_MUTEX_INITIALIZER;
michael@0 44
michael@0 45 static UBool U_CALLCONV
michael@0 46 uspoof_cleanup(void) {
michael@0 47 delete gInclusionSet;
michael@0 48 gInclusionSet = NULL;
michael@0 49 delete gRecommendedSet;
michael@0 50 gRecommendedSet = NULL;
michael@0 51 gNfdNormalizer = NULL;
michael@0 52 return TRUE;
michael@0 53 }
michael@0 54
michael@0 55 static void initializeStatics() {
michael@0 56 Mutex m(&gInitMutex);
michael@0 57 UErrorCode status = U_ZERO_ERROR;
michael@0 58 if (gInclusionSet == NULL) {
michael@0 59 gInclusionSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\
michael@0 60 \\-.\\u00B7\\u05F3\\u05F4\\u0F0B\\u200C\\u200D\\u2019]"), status);
michael@0 61 gRecommendedSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\
michael@0 62 [0-z\\u00C0-\\u017E\\u01A0\\u01A1\\u01AF\\u01B0\\u01CD-\
michael@0 63 \\u01DC\\u01DE-\\u01E3\\u01E6-\\u01F5\\u01F8-\\u021B\\u021E\
michael@0 64 \\u021F\\u0226-\\u0233\\u02BB\\u02BC\\u02EC\\u0300-\\u0304\
michael@0 65 \\u0306-\\u030C\\u030F-\\u0311\\u0313\\u0314\\u031B\\u0323-\
michael@0 66 \\u0328\\u032D\\u032E\\u0330\\u0331\\u0335\\u0338\\u0339\
michael@0 67 \\u0342-\\u0345\\u037B-\\u03CE\\u03FC-\\u045F\\u048A-\\u0525\
michael@0 68 \\u0531-\\u0586\\u05D0-\\u05F2\\u0621-\\u063F\\u0641-\\u0655\
michael@0 69 \\u0660-\\u0669\\u0670-\\u068D\\u068F-\\u06D5\\u06E5\\u06E6\
michael@0 70 \\u06EE-\\u06FF\\u0750-\\u07B1\\u0901-\\u0939\\u093C-\\u094D\
michael@0 71 \\u0950\\u0960-\\u0972\\u0979-\\u0A4D\\u0A5C-\\u0A74\\u0A81-\
michael@0 72 \\u0B43\\u0B47-\\u0B61\\u0B66-\\u0C56\\u0C60\\u0C61\\u0C66-\
michael@0 73 \\u0CD6\\u0CE0-\\u0CEF\\u0D02-\\u0D28\\u0D2A-\\u0D39\\u0D3D-\
michael@0 74 \\u0D43\\u0D46-\\u0D4D\\u0D57-\\u0D61\\u0D66-\\u0D8E\\u0D91-\
michael@0 75 \\u0DA5\\u0DA7-\\u0DDE\\u0DF2\\u0E01-\\u0ED9\\u0F00\\u0F20-\
michael@0 76 \\u0F8B\\u0F90-\\u109D\\u10D0-\\u10F0\\u10F7-\\u10FA\\u1200-\
michael@0 77 \\u135A\\u135F\\u1380-\\u138F\\u1401-\\u167F\\u1780-\\u17A2\
michael@0 78 \\u17A5-\\u17A7\\u17A9-\\u17B3\\u17B6-\\u17CA\\u17D2\\u17D7-\
michael@0 79 \\u17DC\\u17E0-\\u17E9\\u1810-\\u18A8\\u18AA-\\u18F5\\u1E00-\
michael@0 80 \\u1E99\\u1F00-\\u1FFC\\u2D30-\\u2D65\\u2D80-\\u2DDE\\u3005-\
michael@0 81 \\u3007\\u3041-\\u31B7\\u3400-\\u9FCB\\uA000-\\uA48C\\uA67F\
michael@0 82 \\uA717-\\uA71F\\uA788\\uAA60-\\uAA7B\\uAC00-\\uD7A3\\uFA0E-\
michael@0 83 \\uFA29\\U00020000-\
michael@0 84 \\U0002B734]-[[:Cn:][:nfkcqc=n:][:XIDC=n:]]]"), status);
michael@0 85 gNfdNormalizer = Normalizer2::getNFDInstance(status);
michael@0 86 }
michael@0 87 ucln_i18n_registerCleanup(UCLN_I18N_SPOOF, uspoof_cleanup);
michael@0 88
michael@0 89 return;
michael@0 90 }
michael@0 91
michael@0 92
michael@0 93 U_CAPI USpoofChecker * U_EXPORT2
michael@0 94 uspoof_open(UErrorCode *status) {
michael@0 95 if (U_FAILURE(*status)) {
michael@0 96 return NULL;
michael@0 97 }
michael@0 98 initializeStatics();
michael@0 99 SpoofImpl *si = new SpoofImpl(SpoofData::getDefault(*status), *status);
michael@0 100 if (U_FAILURE(*status)) {
michael@0 101 delete si;
michael@0 102 si = NULL;
michael@0 103 }
michael@0 104 return reinterpret_cast<USpoofChecker *>(si);
michael@0 105 }
michael@0 106
michael@0 107
michael@0 108 U_CAPI USpoofChecker * U_EXPORT2
michael@0 109 uspoof_openFromSerialized(const void *data, int32_t length, int32_t *pActualLength,
michael@0 110 UErrorCode *status) {
michael@0 111 if (U_FAILURE(*status)) {
michael@0 112 return NULL;
michael@0 113 }
michael@0 114 initializeStatics();
michael@0 115 SpoofData *sd = new SpoofData(data, length, *status);
michael@0 116 SpoofImpl *si = new SpoofImpl(sd, *status);
michael@0 117 if (U_FAILURE(*status)) {
michael@0 118 delete sd;
michael@0 119 delete si;
michael@0 120 return NULL;
michael@0 121 }
michael@0 122 if (sd == NULL || si == NULL) {
michael@0 123 *status = U_MEMORY_ALLOCATION_ERROR;
michael@0 124 delete sd;
michael@0 125 delete si;
michael@0 126 return NULL;
michael@0 127 }
michael@0 128
michael@0 129 if (pActualLength != NULL) {
michael@0 130 *pActualLength = sd->fRawData->fLength;
michael@0 131 }
michael@0 132 return reinterpret_cast<USpoofChecker *>(si);
michael@0 133 }
michael@0 134
michael@0 135
michael@0 136 U_CAPI USpoofChecker * U_EXPORT2
michael@0 137 uspoof_clone(const USpoofChecker *sc, UErrorCode *status) {
michael@0 138 const SpoofImpl *src = SpoofImpl::validateThis(sc, *status);
michael@0 139 if (src == NULL) {
michael@0 140 return NULL;
michael@0 141 }
michael@0 142 SpoofImpl *result = new SpoofImpl(*src, *status); // copy constructor
michael@0 143 if (U_FAILURE(*status)) {
michael@0 144 delete result;
michael@0 145 result = NULL;
michael@0 146 }
michael@0 147 return reinterpret_cast<USpoofChecker *>(result);
michael@0 148 }
michael@0 149
michael@0 150
michael@0 151 U_CAPI void U_EXPORT2
michael@0 152 uspoof_close(USpoofChecker *sc) {
michael@0 153 UErrorCode status = U_ZERO_ERROR;
michael@0 154 SpoofImpl *This = SpoofImpl::validateThis(sc, status);
michael@0 155 delete This;
michael@0 156 }
michael@0 157
michael@0 158
michael@0 159 U_CAPI void U_EXPORT2
michael@0 160 uspoof_setChecks(USpoofChecker *sc, int32_t checks, UErrorCode *status) {
michael@0 161 SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 162 if (This == NULL) {
michael@0 163 return;
michael@0 164 }
michael@0 165
michael@0 166 // Verify that the requested checks are all ones (bits) that
michael@0 167 // are acceptable, known values.
michael@0 168 if (checks & ~USPOOF_ALL_CHECKS) {
michael@0 169 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 170 return;
michael@0 171 }
michael@0 172
michael@0 173 This->fChecks = checks;
michael@0 174 }
michael@0 175
michael@0 176
michael@0 177 U_CAPI int32_t U_EXPORT2
michael@0 178 uspoof_getChecks(const USpoofChecker *sc, UErrorCode *status) {
michael@0 179 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 180 if (This == NULL) {
michael@0 181 return 0;
michael@0 182 }
michael@0 183 return This->fChecks;
michael@0 184 }
michael@0 185
michael@0 186 U_CAPI void U_EXPORT2
michael@0 187 uspoof_setRestrictionLevel(USpoofChecker *sc, URestrictionLevel restrictionLevel) {
michael@0 188 UErrorCode status = U_ZERO_ERROR;
michael@0 189 SpoofImpl *This = SpoofImpl::validateThis(sc, status);
michael@0 190 if (This != NULL) {
michael@0 191 This->fRestrictionLevel = restrictionLevel;
michael@0 192 }
michael@0 193 }
michael@0 194
michael@0 195 U_CAPI URestrictionLevel U_EXPORT2
michael@0 196 uspoof_getRestrictionLevel(const USpoofChecker *sc) {
michael@0 197 UErrorCode status = U_ZERO_ERROR;
michael@0 198 const SpoofImpl *This = SpoofImpl::validateThis(sc, status);
michael@0 199 if (This == NULL) {
michael@0 200 return USPOOF_UNRESTRICTIVE;
michael@0 201 }
michael@0 202 return This->fRestrictionLevel;
michael@0 203 }
michael@0 204
michael@0 205 U_CAPI void U_EXPORT2
michael@0 206 uspoof_setAllowedLocales(USpoofChecker *sc, const char *localesList, UErrorCode *status) {
michael@0 207 SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 208 if (This == NULL) {
michael@0 209 return;
michael@0 210 }
michael@0 211 This->setAllowedLocales(localesList, *status);
michael@0 212 }
michael@0 213
michael@0 214 U_CAPI const char * U_EXPORT2
michael@0 215 uspoof_getAllowedLocales(USpoofChecker *sc, UErrorCode *status) {
michael@0 216 SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 217 if (This == NULL) {
michael@0 218 return NULL;
michael@0 219 }
michael@0 220 return This->getAllowedLocales(*status);
michael@0 221 }
michael@0 222
michael@0 223
michael@0 224 U_CAPI const USet * U_EXPORT2
michael@0 225 uspoof_getAllowedChars(const USpoofChecker *sc, UErrorCode *status) {
michael@0 226 const UnicodeSet *result = uspoof_getAllowedUnicodeSet(sc, status);
michael@0 227 return result->toUSet();
michael@0 228 }
michael@0 229
michael@0 230 U_CAPI const UnicodeSet * U_EXPORT2
michael@0 231 uspoof_getAllowedUnicodeSet(const USpoofChecker *sc, UErrorCode *status) {
michael@0 232 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 233 if (This == NULL) {
michael@0 234 return NULL;
michael@0 235 }
michael@0 236 return This->fAllowedCharsSet;
michael@0 237 }
michael@0 238
michael@0 239
michael@0 240 U_CAPI void U_EXPORT2
michael@0 241 uspoof_setAllowedChars(USpoofChecker *sc, const USet *chars, UErrorCode *status) {
michael@0 242 const UnicodeSet *set = UnicodeSet::fromUSet(chars);
michael@0 243 uspoof_setAllowedUnicodeSet(sc, set, status);
michael@0 244 }
michael@0 245
michael@0 246
michael@0 247 U_CAPI void U_EXPORT2
michael@0 248 uspoof_setAllowedUnicodeSet(USpoofChecker *sc, const UnicodeSet *chars, UErrorCode *status) {
michael@0 249 SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 250 if (This == NULL) {
michael@0 251 return;
michael@0 252 }
michael@0 253 if (chars->isBogus()) {
michael@0 254 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 255 return;
michael@0 256 }
michael@0 257 UnicodeSet *clonedSet = static_cast<UnicodeSet *>(chars->clone());
michael@0 258 if (clonedSet == NULL || clonedSet->isBogus()) {
michael@0 259 *status = U_MEMORY_ALLOCATION_ERROR;
michael@0 260 return;
michael@0 261 }
michael@0 262 clonedSet->freeze();
michael@0 263 delete This->fAllowedCharsSet;
michael@0 264 This->fAllowedCharsSet = clonedSet;
michael@0 265 This->fChecks |= USPOOF_CHAR_LIMIT;
michael@0 266 }
michael@0 267
michael@0 268
michael@0 269 U_CAPI int32_t U_EXPORT2
michael@0 270 uspoof_check(const USpoofChecker *sc,
michael@0 271 const UChar *id, int32_t length,
michael@0 272 int32_t *position,
michael@0 273 UErrorCode *status) {
michael@0 274
michael@0 275 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 276 if (This == NULL) {
michael@0 277 return 0;
michael@0 278 }
michael@0 279 if (length < -1) {
michael@0 280 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 281 return 0;
michael@0 282 }
michael@0 283 UnicodeString idStr((length == -1), id, length); // Aliasing constructor.
michael@0 284 int32_t result = uspoof_checkUnicodeString(sc, idStr, position, status);
michael@0 285 return result;
michael@0 286 }
michael@0 287
michael@0 288
michael@0 289 U_CAPI int32_t U_EXPORT2
michael@0 290 uspoof_checkUTF8(const USpoofChecker *sc,
michael@0 291 const char *id, int32_t length,
michael@0 292 int32_t *position,
michael@0 293 UErrorCode *status) {
michael@0 294
michael@0 295 if (U_FAILURE(*status)) {
michael@0 296 return 0;
michael@0 297 }
michael@0 298 UnicodeString idStr = UnicodeString::fromUTF8(StringPiece(id, length>=0 ? length : uprv_strlen(id)));
michael@0 299 int32_t result = uspoof_checkUnicodeString(sc, idStr, position, status);
michael@0 300 return result;
michael@0 301 }
michael@0 302
michael@0 303
michael@0 304 U_CAPI int32_t U_EXPORT2
michael@0 305 uspoof_areConfusable(const USpoofChecker *sc,
michael@0 306 const UChar *id1, int32_t length1,
michael@0 307 const UChar *id2, int32_t length2,
michael@0 308 UErrorCode *status) {
michael@0 309 SpoofImpl::validateThis(sc, *status);
michael@0 310 if (U_FAILURE(*status)) {
michael@0 311 return 0;
michael@0 312 }
michael@0 313 if (length1 < -1 || length2 < -1) {
michael@0 314 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 315 return 0;
michael@0 316 }
michael@0 317
michael@0 318 UnicodeString id1Str((length1==-1), id1, length1); // Aliasing constructor
michael@0 319 UnicodeString id2Str((length2==-1), id2, length2); // Aliasing constructor
michael@0 320 return uspoof_areConfusableUnicodeString(sc, id1Str, id2Str, status);
michael@0 321 }
michael@0 322
michael@0 323
michael@0 324 U_CAPI int32_t U_EXPORT2
michael@0 325 uspoof_areConfusableUTF8(const USpoofChecker *sc,
michael@0 326 const char *id1, int32_t length1,
michael@0 327 const char *id2, int32_t length2,
michael@0 328 UErrorCode *status) {
michael@0 329 SpoofImpl::validateThis(sc, *status);
michael@0 330 if (U_FAILURE(*status)) {
michael@0 331 return 0;
michael@0 332 }
michael@0 333 if (length1 < -1 || length2 < -1) {
michael@0 334 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 335 return 0;
michael@0 336 }
michael@0 337 UnicodeString id1Str = UnicodeString::fromUTF8(StringPiece(id1, length1>=0? length1 : uprv_strlen(id1)));
michael@0 338 UnicodeString id2Str = UnicodeString::fromUTF8(StringPiece(id2, length2>=0? length2 : uprv_strlen(id2)));
michael@0 339 int32_t results = uspoof_areConfusableUnicodeString(sc, id1Str, id2Str, status);
michael@0 340 return results;
michael@0 341 }
michael@0 342
michael@0 343
michael@0 344 U_CAPI int32_t U_EXPORT2
michael@0 345 uspoof_areConfusableUnicodeString(const USpoofChecker *sc,
michael@0 346 const icu::UnicodeString &id1,
michael@0 347 const icu::UnicodeString &id2,
michael@0 348 UErrorCode *status) {
michael@0 349 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 350 if (U_FAILURE(*status)) {
michael@0 351 return 0;
michael@0 352 }
michael@0 353 //
michael@0 354 // See section 4 of UAX 39 for the algorithm for checking whether two strings are confusable,
michael@0 355 // and for definitions of the types (single, whole, mixed-script) of confusables.
michael@0 356
michael@0 357 // We only care about a few of the check flags. Ignore the others.
michael@0 358 // If no tests relavant to this function have been specified, return an error.
michael@0 359 // TODO: is this really the right thing to do? It's probably an error on the caller's part,
michael@0 360 // but logically we would just return 0 (no error).
michael@0 361 if ((This->fChecks & (USPOOF_SINGLE_SCRIPT_CONFUSABLE | USPOOF_MIXED_SCRIPT_CONFUSABLE |
michael@0 362 USPOOF_WHOLE_SCRIPT_CONFUSABLE)) == 0) {
michael@0 363 *status = U_INVALID_STATE_ERROR;
michael@0 364 return 0;
michael@0 365 }
michael@0 366 int32_t flagsForSkeleton = This->fChecks & USPOOF_ANY_CASE;
michael@0 367
michael@0 368 int32_t result = 0;
michael@0 369 IdentifierInfo *identifierInfo = This->getIdentifierInfo(*status);
michael@0 370 if (U_FAILURE(*status)) {
michael@0 371 return 0;
michael@0 372 }
michael@0 373 identifierInfo->setIdentifier(id1, *status);
michael@0 374 int32_t id1ScriptCount = identifierInfo->getScriptCount();
michael@0 375 identifierInfo->setIdentifier(id2, *status);
michael@0 376 int32_t id2ScriptCount = identifierInfo->getScriptCount();
michael@0 377 This->releaseIdentifierInfo(identifierInfo);
michael@0 378 identifierInfo = NULL;
michael@0 379
michael@0 380 if (This->fChecks & USPOOF_SINGLE_SCRIPT_CONFUSABLE) {
michael@0 381 UnicodeString id1Skeleton;
michael@0 382 UnicodeString id2Skeleton;
michael@0 383 if (id1ScriptCount <= 1 && id2ScriptCount <= 1) {
michael@0 384 flagsForSkeleton |= USPOOF_SINGLE_SCRIPT_CONFUSABLE;
michael@0 385 uspoof_getSkeletonUnicodeString(sc, flagsForSkeleton, id1, id1Skeleton, status);
michael@0 386 uspoof_getSkeletonUnicodeString(sc, flagsForSkeleton, id2, id2Skeleton, status);
michael@0 387 if (id1Skeleton == id2Skeleton) {
michael@0 388 result |= USPOOF_SINGLE_SCRIPT_CONFUSABLE;
michael@0 389 }
michael@0 390 }
michael@0 391 }
michael@0 392
michael@0 393 if (result & USPOOF_SINGLE_SCRIPT_CONFUSABLE) {
michael@0 394 // If the two inputs are single script confusable they cannot also be
michael@0 395 // mixed or whole script confusable, according to the UAX39 definitions.
michael@0 396 // So we can skip those tests.
michael@0 397 return result;
michael@0 398 }
michael@0 399
michael@0 400 // Two identifiers are whole script confusable if each is of a single script
michael@0 401 // and they are mixed script confusable.
michael@0 402 UBool possiblyWholeScriptConfusables =
michael@0 403 id1ScriptCount <= 1 && id2ScriptCount <= 1 && (This->fChecks & USPOOF_WHOLE_SCRIPT_CONFUSABLE);
michael@0 404
michael@0 405 //
michael@0 406 // Mixed Script Check
michael@0 407 //
michael@0 408 if ((This->fChecks & USPOOF_MIXED_SCRIPT_CONFUSABLE) || possiblyWholeScriptConfusables ) {
michael@0 409 // For getSkeleton(), resetting the USPOOF_SINGLE_SCRIPT_CONFUSABLE flag will get us
michael@0 410 // the mixed script table skeleton, which is what we want.
michael@0 411 // The Any Case / Lower Case bit in the skelton flags was set at the top of the function.
michael@0 412 UnicodeString id1Skeleton;
michael@0 413 UnicodeString id2Skeleton;
michael@0 414 flagsForSkeleton &= ~USPOOF_SINGLE_SCRIPT_CONFUSABLE;
michael@0 415 uspoof_getSkeletonUnicodeString(sc, flagsForSkeleton, id1, id1Skeleton, status);
michael@0 416 uspoof_getSkeletonUnicodeString(sc, flagsForSkeleton, id2, id2Skeleton, status);
michael@0 417 if (id1Skeleton == id2Skeleton) {
michael@0 418 result |= USPOOF_MIXED_SCRIPT_CONFUSABLE;
michael@0 419 if (possiblyWholeScriptConfusables) {
michael@0 420 result |= USPOOF_WHOLE_SCRIPT_CONFUSABLE;
michael@0 421 }
michael@0 422 }
michael@0 423 }
michael@0 424
michael@0 425 return result;
michael@0 426 }
michael@0 427
michael@0 428
michael@0 429
michael@0 430
michael@0 431 U_CAPI int32_t U_EXPORT2
michael@0 432 uspoof_checkUnicodeString(const USpoofChecker *sc,
michael@0 433 const icu::UnicodeString &id,
michael@0 434 int32_t *position,
michael@0 435 UErrorCode *status) {
michael@0 436 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 437 if (This == NULL) {
michael@0 438 return 0;
michael@0 439 }
michael@0 440 int32_t result = 0;
michael@0 441
michael@0 442 IdentifierInfo *identifierInfo = NULL;
michael@0 443 if ((This->fChecks) & (USPOOF_RESTRICTION_LEVEL | USPOOF_MIXED_NUMBERS)) {
michael@0 444 identifierInfo = This->getIdentifierInfo(*status);
michael@0 445 if (U_FAILURE(*status)) {
michael@0 446 goto cleanupAndReturn;
michael@0 447 }
michael@0 448 identifierInfo->setIdentifier(id, *status);
michael@0 449 identifierInfo->setIdentifierProfile(*This->fAllowedCharsSet);
michael@0 450 }
michael@0 451
michael@0 452
michael@0 453 if ((This->fChecks) & USPOOF_RESTRICTION_LEVEL) {
michael@0 454 URestrictionLevel idRestrictionLevel = identifierInfo->getRestrictionLevel(*status);
michael@0 455 if (idRestrictionLevel > This->fRestrictionLevel) {
michael@0 456 result |= USPOOF_RESTRICTION_LEVEL;
michael@0 457 }
michael@0 458 if (This->fChecks & USPOOF_AUX_INFO) {
michael@0 459 result |= idRestrictionLevel;
michael@0 460 }
michael@0 461 }
michael@0 462
michael@0 463 if ((This->fChecks) & USPOOF_MIXED_NUMBERS) {
michael@0 464 const UnicodeSet *numerics = identifierInfo->getNumerics();
michael@0 465 if (numerics->size() > 1) {
michael@0 466 result |= USPOOF_MIXED_NUMBERS;
michael@0 467 }
michael@0 468
michael@0 469 // TODO: ICU4J returns the UnicodeSet of the numerics found in the identifier.
michael@0 470 // We have no easy way to do the same in C.
michael@0 471 // if (checkResult != null) {
michael@0 472 // checkResult.numerics = numerics;
michael@0 473 // }
michael@0 474 }
michael@0 475
michael@0 476
michael@0 477 if (This->fChecks & (USPOOF_CHAR_LIMIT)) {
michael@0 478 int32_t i;
michael@0 479 UChar32 c;
michael@0 480 int32_t length = id.length();
michael@0 481 for (i=0; i<length ;) {
michael@0 482 c = id.char32At(i);
michael@0 483 i += U16_LENGTH(c);
michael@0 484 if (!This->fAllowedCharsSet->contains(c)) {
michael@0 485 result |= USPOOF_CHAR_LIMIT;
michael@0 486 break;
michael@0 487 }
michael@0 488 }
michael@0 489 }
michael@0 490
michael@0 491 if (This->fChecks &
michael@0 492 (USPOOF_WHOLE_SCRIPT_CONFUSABLE | USPOOF_MIXED_SCRIPT_CONFUSABLE | USPOOF_INVISIBLE)) {
michael@0 493 // These are the checks that need to be done on NFD input
michael@0 494 UnicodeString nfdText;
michael@0 495 gNfdNormalizer->normalize(id, nfdText, *status);
michael@0 496 int32_t nfdLength = nfdText.length();
michael@0 497
michael@0 498 if (This->fChecks & USPOOF_INVISIBLE) {
michael@0 499
michael@0 500 // scan for more than one occurence of the same non-spacing mark
michael@0 501 // in a sequence of non-spacing marks.
michael@0 502 int32_t i;
michael@0 503 UChar32 c;
michael@0 504 UChar32 firstNonspacingMark = 0;
michael@0 505 UBool haveMultipleMarks = FALSE;
michael@0 506 UnicodeSet marksSeenSoFar; // Set of combining marks in a single combining sequence.
michael@0 507
michael@0 508 for (i=0; i<nfdLength ;) {
michael@0 509 c = nfdText.char32At(i);
michael@0 510 i += U16_LENGTH(c);
michael@0 511 if (u_charType(c) != U_NON_SPACING_MARK) {
michael@0 512 firstNonspacingMark = 0;
michael@0 513 if (haveMultipleMarks) {
michael@0 514 marksSeenSoFar.clear();
michael@0 515 haveMultipleMarks = FALSE;
michael@0 516 }
michael@0 517 continue;
michael@0 518 }
michael@0 519 if (firstNonspacingMark == 0) {
michael@0 520 firstNonspacingMark = c;
michael@0 521 continue;
michael@0 522 }
michael@0 523 if (!haveMultipleMarks) {
michael@0 524 marksSeenSoFar.add(firstNonspacingMark);
michael@0 525 haveMultipleMarks = TRUE;
michael@0 526 }
michael@0 527 if (marksSeenSoFar.contains(c)) {
michael@0 528 // report the error, and stop scanning.
michael@0 529 // No need to find more than the first failure.
michael@0 530 result |= USPOOF_INVISIBLE;
michael@0 531 break;
michael@0 532 }
michael@0 533 marksSeenSoFar.add(c);
michael@0 534 }
michael@0 535 }
michael@0 536
michael@0 537
michael@0 538 if (This->fChecks & (USPOOF_WHOLE_SCRIPT_CONFUSABLE | USPOOF_MIXED_SCRIPT_CONFUSABLE)) {
michael@0 539 // The basic test is the same for both whole and mixed script confusables.
michael@0 540 // Compute the set of scripts that every input character has a confusable in.
michael@0 541 // For this computation an input character is always considered to be
michael@0 542 // confusable with itself in its own script.
michael@0 543 //
michael@0 544 // If the number of such scripts is two or more, and the input consisted of
michael@0 545 // characters all from a single script, we have a whole script confusable.
michael@0 546 // (The two scripts will be the original script and the one that is confusable)
michael@0 547 //
michael@0 548 // If the number of such scripts >= one, and the original input contained characters from
michael@0 549 // more than one script, we have a mixed script confusable. (We can transform
michael@0 550 // some of the characters, and end up with a visually similar string all in
michael@0 551 // one script.)
michael@0 552
michael@0 553 if (identifierInfo == NULL) {
michael@0 554 identifierInfo = This->getIdentifierInfo(*status);
michael@0 555 if (U_FAILURE(*status)) {
michael@0 556 goto cleanupAndReturn;
michael@0 557 }
michael@0 558 identifierInfo->setIdentifier(id, *status);
michael@0 559 }
michael@0 560
michael@0 561 int32_t scriptCount = identifierInfo->getScriptCount();
michael@0 562
michael@0 563 ScriptSet scripts;
michael@0 564 This->wholeScriptCheck(nfdText, &scripts, *status);
michael@0 565 int32_t confusableScriptCount = scripts.countMembers();
michael@0 566 //printf("confusableScriptCount = %d\n", confusableScriptCount);
michael@0 567
michael@0 568 if ((This->fChecks & USPOOF_WHOLE_SCRIPT_CONFUSABLE) &&
michael@0 569 confusableScriptCount >= 2 &&
michael@0 570 scriptCount == 1) {
michael@0 571 result |= USPOOF_WHOLE_SCRIPT_CONFUSABLE;
michael@0 572 }
michael@0 573
michael@0 574 if ((This->fChecks & USPOOF_MIXED_SCRIPT_CONFUSABLE) &&
michael@0 575 confusableScriptCount >= 1 &&
michael@0 576 scriptCount > 1) {
michael@0 577 result |= USPOOF_MIXED_SCRIPT_CONFUSABLE;
michael@0 578 }
michael@0 579 }
michael@0 580 }
michael@0 581
michael@0 582 cleanupAndReturn:
michael@0 583 This->releaseIdentifierInfo(identifierInfo);
michael@0 584 if (position != NULL) {
michael@0 585 *position = 0;
michael@0 586 }
michael@0 587 return result;
michael@0 588 }
michael@0 589
michael@0 590
michael@0 591 U_CAPI int32_t U_EXPORT2
michael@0 592 uspoof_getSkeleton(const USpoofChecker *sc,
michael@0 593 uint32_t type,
michael@0 594 const UChar *id, int32_t length,
michael@0 595 UChar *dest, int32_t destCapacity,
michael@0 596 UErrorCode *status) {
michael@0 597
michael@0 598 SpoofImpl::validateThis(sc, *status);
michael@0 599 if (U_FAILURE(*status)) {
michael@0 600 return 0;
michael@0 601 }
michael@0 602 if (length<-1 || destCapacity<0 || (destCapacity==0 && dest!=NULL)) {
michael@0 603 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 604 return 0;
michael@0 605 }
michael@0 606
michael@0 607 UnicodeString idStr((length==-1), id, length); // Aliasing constructor
michael@0 608 UnicodeString destStr;
michael@0 609 uspoof_getSkeletonUnicodeString(sc, type, idStr, destStr, status);
michael@0 610 destStr.extract(dest, destCapacity, *status);
michael@0 611 return destStr.length();
michael@0 612 }
michael@0 613
michael@0 614
michael@0 615
michael@0 616 U_I18N_API UnicodeString & U_EXPORT2
michael@0 617 uspoof_getSkeletonUnicodeString(const USpoofChecker *sc,
michael@0 618 uint32_t type,
michael@0 619 const UnicodeString &id,
michael@0 620 UnicodeString &dest,
michael@0 621 UErrorCode *status) {
michael@0 622 const SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 623 if (U_FAILURE(*status)) {
michael@0 624 return dest;
michael@0 625 }
michael@0 626
michael@0 627 int32_t tableMask = 0;
michael@0 628 switch (type) {
michael@0 629 case 0:
michael@0 630 tableMask = USPOOF_ML_TABLE_FLAG;
michael@0 631 break;
michael@0 632 case USPOOF_SINGLE_SCRIPT_CONFUSABLE:
michael@0 633 tableMask = USPOOF_SL_TABLE_FLAG;
michael@0 634 break;
michael@0 635 case USPOOF_ANY_CASE:
michael@0 636 tableMask = USPOOF_MA_TABLE_FLAG;
michael@0 637 break;
michael@0 638 case USPOOF_SINGLE_SCRIPT_CONFUSABLE | USPOOF_ANY_CASE:
michael@0 639 tableMask = USPOOF_SA_TABLE_FLAG;
michael@0 640 break;
michael@0 641 default:
michael@0 642 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 643 return dest;
michael@0 644 }
michael@0 645
michael@0 646 UnicodeString nfdId;
michael@0 647 gNfdNormalizer->normalize(id, nfdId, *status);
michael@0 648
michael@0 649 // Apply the skeleton mapping to the NFD normalized input string
michael@0 650 // Accumulate the skeleton, possibly unnormalized, in a UnicodeString.
michael@0 651 int32_t inputIndex = 0;
michael@0 652 UnicodeString skelStr;
michael@0 653 int32_t normalizedLen = nfdId.length();
michael@0 654 for (inputIndex=0; inputIndex < normalizedLen; ) {
michael@0 655 UChar32 c = nfdId.char32At(inputIndex);
michael@0 656 inputIndex += U16_LENGTH(c);
michael@0 657 This->confusableLookup(c, tableMask, skelStr);
michael@0 658 }
michael@0 659
michael@0 660 gNfdNormalizer->normalize(skelStr, dest, *status);
michael@0 661 return dest;
michael@0 662 }
michael@0 663
michael@0 664
michael@0 665 U_CAPI int32_t U_EXPORT2
michael@0 666 uspoof_getSkeletonUTF8(const USpoofChecker *sc,
michael@0 667 uint32_t type,
michael@0 668 const char *id, int32_t length,
michael@0 669 char *dest, int32_t destCapacity,
michael@0 670 UErrorCode *status) {
michael@0 671 SpoofImpl::validateThis(sc, *status);
michael@0 672 if (U_FAILURE(*status)) {
michael@0 673 return 0;
michael@0 674 }
michael@0 675 if (length<-1 || destCapacity<0 || (destCapacity==0 && dest!=NULL)) {
michael@0 676 *status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 677 return 0;
michael@0 678 }
michael@0 679
michael@0 680 UnicodeString srcStr = UnicodeString::fromUTF8(StringPiece(id, length>=0 ? length : uprv_strlen(id)));
michael@0 681 UnicodeString destStr;
michael@0 682 uspoof_getSkeletonUnicodeString(sc, type, srcStr, destStr, status);
michael@0 683 if (U_FAILURE(*status)) {
michael@0 684 return 0;
michael@0 685 }
michael@0 686
michael@0 687 int32_t lengthInUTF8 = 0;
michael@0 688 u_strToUTF8(dest, destCapacity, &lengthInUTF8,
michael@0 689 destStr.getBuffer(), destStr.length(), status);
michael@0 690 return lengthInUTF8;
michael@0 691 }
michael@0 692
michael@0 693
michael@0 694 U_CAPI int32_t U_EXPORT2
michael@0 695 uspoof_serialize(USpoofChecker *sc,void *buf, int32_t capacity, UErrorCode *status) {
michael@0 696 SpoofImpl *This = SpoofImpl::validateThis(sc, *status);
michael@0 697 if (This == NULL) {
michael@0 698 U_ASSERT(U_FAILURE(*status));
michael@0 699 return 0;
michael@0 700 }
michael@0 701 int32_t dataSize = This->fSpoofData->fRawData->fLength;
michael@0 702 if (capacity < dataSize) {
michael@0 703 *status = U_BUFFER_OVERFLOW_ERROR;
michael@0 704 return dataSize;
michael@0 705 }
michael@0 706 uprv_memcpy(buf, This->fSpoofData->fRawData, dataSize);
michael@0 707 return dataSize;
michael@0 708 }
michael@0 709
michael@0 710 U_CAPI const USet * U_EXPORT2
michael@0 711 uspoof_getInclusionSet(UErrorCode *) {
michael@0 712 initializeStatics();
michael@0 713 return gInclusionSet->toUSet();
michael@0 714 }
michael@0 715
michael@0 716 U_CAPI const USet * U_EXPORT2
michael@0 717 uspoof_getRecommendedSet(UErrorCode *) {
michael@0 718 initializeStatics();
michael@0 719 return gRecommendedSet->toUSet();
michael@0 720 }
michael@0 721
michael@0 722 U_I18N_API const UnicodeSet * U_EXPORT2
michael@0 723 uspoof_getInclusionUnicodeSet(UErrorCode *) {
michael@0 724 initializeStatics();
michael@0 725 return gInclusionSet;
michael@0 726 }
michael@0 727
michael@0 728 U_I18N_API const UnicodeSet * U_EXPORT2
michael@0 729 uspoof_getRecommendedUnicodeSet(UErrorCode *) {
michael@0 730 initializeStatics();
michael@0 731 return gRecommendedSet;
michael@0 732 }
michael@0 733
michael@0 734
michael@0 735
michael@0 736 #endif // !UCONFIG_NO_NORMALIZATION

mercurial