Coverage Report

Created: 2018-09-25 14:53

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