1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4 *******************************************************************************
5 *
6 *   Copyright (C) 2005-2014, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *
11 *   created on: 2005jun15
12 *   created by: Raymond Yang
13 */
14
15#include "unicode/utypes.h"
16
17#if !UCONFIG_NO_IDNA
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include "unicode/ucnv.h"
23#include "unicode/ustring.h"
24#include "unicode/uidna.h"
25#include "unicode/utf16.h"
26#include "idnaconf.h"
27#include "charstr.h"
28
29static const UChar C_TAG[] = {0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0}; // =====
30static const UChar C_NAMEZONE[] = {0x6E, 0x61, 0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0}; // namezone
31static const UChar C_NAMEBASE[] = {0x6E, 0x61, 0x6D, 0x65, 0x62, 0x61, 0x73, 0x65, 0}; // namebase
32
33static const UChar C_TYPE[] = {0x74, 0x79, 0x70, 0x65, 0}; // type
34static const UChar C_TOASCII[]  =  {0x74, 0x6F, 0x61, 0x73, 0x63, 0x69, 0x69, 0};       // toascii
35static const UChar C_TOUNICODE[] = {0x74, 0x6F, 0x75, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0}; // tounicode
36
37static const UChar C_PASSFAIL[] = {0x70, 0x61, 0x73, 0x73, 0x66, 0x61, 0x69, 0x6C, 0}; // passfail
38static const UChar C_PASS[] = {0x70, 0x61, 0x73, 0x73, 0}; // pass
39static const UChar C_FAIL[] = {0x66, 0x61, 0x69, 0x6C, 0}; // fail
40
41static const UChar C_DESC[] = {0x64, 0x65, 0x73, 0x63, 0}; // desc
42static const UChar C_USESTD3ASCIIRULES[] = {0x55, 0x73, 0x65, 0x53, 0x54, 0x44,
43       0x33, 0x41, 0x53, 0x43, 0x49, 0x49, 0x52, 0x75, 0x6C, 0x65, 0x73, 0}; // UseSTD3ASCIIRules
44
45IdnaConfTest::IdnaConfTest(){
46    base = NULL;
47    len = 0;
48    curOffset = 0;
49
50    type = option = passfail = -1;
51    namebase.setToBogus();
52    namezone.setToBogus();
53}
54IdnaConfTest::~IdnaConfTest(){
55    delete [] base;
56}
57
58#if !UCONFIG_NO_IDNA
59
60int IdnaConfTest::isNewlineMark(){
61    static const UChar LF        = 0x0a;
62    static const UChar CR        = 0x0d;
63    UChar c = base[curOffset];
64    // CR LF
65    if ( c == CR && curOffset + 1 < len && base[curOffset + 1] == LF){
66        return 2;
67    }
68
69    // CR or LF
70    if ( c == CR || c == LF) {
71        return 1;
72    }
73
74    return 0;
75}
76
77/* Read a logical line.
78 *
79 * All lines ending in a backslash (\) and immediately followed by a newline
80 * character are joined with the next line in the source file forming logical
81 * lines from the physical lines.
82 *
83 */
84UBool IdnaConfTest::ReadOneLine(UnicodeString& buf){
85    if ( !(curOffset < len) ) return false; // stream end
86
87    static const UChar BACKSLASH = 0x5c;
88    buf.remove();
89    int t = 0;
90    while (curOffset < len){
91        if ((t = isNewlineMark())) {  // end of line
92            curOffset += t;
93            break;
94        }
95        UChar c = base[curOffset];
96        if (c == BACKSLASH && curOffset < len -1){  // escaped new line mark
97            if ((t = isNewlineMark())){
98                curOffset += 1 + t;  // BACKSLAH and NewlineMark
99                continue;
100            }
101        }
102        buf.append(c);
103        curOffset++;
104    }
105    return true;
106}
107
108//
109//===============================================================
110//
111
112/* Explain <xxxxx> tag to a native value
113 *
114 * Since <xxxxx> is always larger than the native value,
115 * the operation will replace the tag directly in the buffer,
116 * and, of course, will shift tail elements.
117 */
118void IdnaConfTest::ExplainCodePointTag(UnicodeString& buf){
119    buf.append((UChar)0);    // add a terminal NULL
120    UChar* bufBase = buf.getBuffer(buf.length());
121    UChar* p = bufBase;
122    while (*p != 0){
123        if ( *p != 0x3C){    // <
124            *bufBase++ = *p++;
125        } else {
126            p++;    // skip <
127            UChar32 cp = 0;
128            for ( ;*p != 0x3E; p++){   // >
129                if (0x30 <= *p && *p <= 0x39){        // 0-9
130                    cp = (cp * 16) + (*p - 0x30);
131                } else if (0x61 <= *p && *p <= 0x66){ // a-f
132                    cp = (cp * 16) + (*p - 0x61) + 10;
133                } else if (0x41 <= *p && *p <= 0x46) {// A-F
134                    cp = (cp * 16) + (*p - 0x41) + 10;
135                }
136                // no else. hope everything is good.
137            }
138            p++;    // skip >
139            if (U_IS_BMP(cp)){
140                *bufBase++ = cp;
141            } else {
142                *bufBase++ = U16_LEAD(cp);
143                *bufBase++ = U16_TRAIL(cp);
144            }
145        }
146    }
147    *bufBase = 0;  // close our buffer
148    buf.releaseBuffer();
149}
150
151void IdnaConfTest::Call(){
152    if (type == -1 || option == -1 || passfail == -1 || namebase.isBogus() || namezone.isBogus()){
153        errln("Incomplete record");
154    } else {
155        UErrorCode status = U_ZERO_ERROR;
156        UChar result[200] = {0,};   // simple life
157        const UChar *p = namebase.getTerminatedBuffer();
158        const int p_len = namebase.length();
159
160        if (type == 0 && option == 0){
161            uidna_IDNToASCII(p, p_len, result, 200, UIDNA_USE_STD3_RULES, NULL, &status);
162        } else if (type == 0 && option == 1){
163            uidna_IDNToASCII(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, NULL, &status);
164        } else if (type == 1 && option == 0){
165            uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_USE_STD3_RULES, NULL, &status);
166        } else if (type == 1 && option == 1){
167            uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, NULL, &status);
168        }
169        if (passfail == 0){
170            if (U_FAILURE(status)){
171                id.append(" should pass, but failed. - ");
172                id.append(u_errorName(status));
173                errcheckln(status, id);
174            } else{
175                if (namezone.compare(result, -1) == 0){
176                    // expected
177                    logln(UnicodeString("namebase: ") + prettify(namebase) + UnicodeString(" result: ") + prettify(result));
178                } else {
179                    id.append(" no error, but result is not as expected.");
180                    errln(id);
181                }
182            }
183        } else if (passfail == 1){
184            if (U_FAILURE(status)){
185                // expected
186                // TODO: Uncomment this when U_IDNA_ZERO_LENGTH_LABEL_ERROR is added to u_errorName
187                //logln("Got the expected error: " + UnicodeString(u_errorName(status)));
188            } else{
189                if (namebase.compare(result, -1) == 0){
190                    // garbage in -> garbage out
191                    logln(UnicodeString("ICU will not recognize malformed ACE-Prefixes or incorrect ACE-Prefixes. ") + UnicodeString("namebase: ") + prettify(namebase) + UnicodeString(" result: ") + prettify(result));
192                } else {
193                    id.append(" should fail, but not failed. ");
194                    id.append(u_errorName(status));
195                    errln(id);
196                }
197            }
198        }
199    }
200    type = option = passfail = -1;
201    namebase.setToBogus();
202    namezone.setToBogus();
203    id.remove();
204    return;
205}
206
207void IdnaConfTest::Test(void){
208    UErrorCode  status  = U_ZERO_ERROR;
209    //
210    //  Open and read the test data file.
211    //
212    const char *testDataDirectory = IntlTest::getSourceTestData(status);
213    CharString testFileName(testDataDirectory, -1, status);
214    testFileName.append("idna_conf.txt", -1, status);
215
216    base = ReadAndConvertFile(testFileName.data(), len, "UTF-8", status);
217    if (U_FAILURE(status)) {
218        return;
219    }
220
221    UnicodeString s;
222    UnicodeString key;
223    UnicodeString value;
224
225    // skip everything before the first "=====" and "=====" itself
226    do {
227        if (!ReadOneLine(s)) {
228            errln("End of file prematurely found");
229            break;
230        }
231    }
232    while (s.compare(C_TAG, -1) != 0);   //"====="
233
234    while(ReadOneLine(s)){
235        s.trim();
236        key.remove();
237        value.remove();
238        if (s.compare(C_TAG, -1) == 0){   //"====="
239            Call();
240       } else {
241            // explain      key:value
242            int p = s.indexOf((UChar)0x3A);    // :
243            key.setTo(s,0,p).trim();
244            value.setTo(s,p+1).trim();
245            if (key.compare(C_TYPE, -1) == 0){
246                if (value.compare(C_TOASCII, -1) == 0) {
247                    type = 0;
248                } else if (value.compare(C_TOUNICODE, -1) == 0){
249                    type = 1;
250                }
251            } else if (key.compare(C_PASSFAIL, -1) == 0){
252                if (value.compare(C_PASS, -1) == 0){
253                    passfail = 0;
254                } else if (value.compare(C_FAIL, -1) == 0){
255                    passfail = 1;
256                }
257            } else if (key.compare(C_DESC, -1) == 0){
258                if (value.indexOf(C_USESTD3ASCIIRULES, u_strlen(C_USESTD3ASCIIRULES), 0) == -1){
259                    option = 1; // not found
260                } else {
261                    option = 0;
262                }
263                id.setTo(value, 0, value.indexOf((UChar)0x20));    // space
264            } else if (key.compare(C_NAMEZONE, -1) == 0){
265                ExplainCodePointTag(value);
266                namezone.setTo(value);
267            } else if (key.compare(C_NAMEBASE, -1) == 0){
268                ExplainCodePointTag(value);
269                namebase.setTo(value);
270            }
271            // just skip other lines
272        }
273    }
274
275    Call(); // for last record
276}
277#else
278void IdnaConfTest::Test(void)
279{
280  // test nothing...
281}
282#endif
283
284void IdnaConfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/){
285    switch (index) {
286        TESTCASE(0,Test);
287        default: name = ""; break;
288    }
289}
290
291#endif
292