• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 static const char16_t C_TAG[] = {0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0}; // =====
30 static const char16_t C_NAMEZONE[] = {0x6E, 0x61, 0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0}; // namezone
31 static const char16_t C_NAMEBASE[] = {0x6E, 0x61, 0x6D, 0x65, 0x62, 0x61, 0x73, 0x65, 0}; // namebase
32 
33 static const char16_t C_TYPE[] = {0x74, 0x79, 0x70, 0x65, 0}; // type
34 static const char16_t C_TOASCII[]  =  {0x74, 0x6F, 0x61, 0x73, 0x63, 0x69, 0x69, 0};       // toascii
35 static const char16_t C_TOUNICODE[] = {0x74, 0x6F, 0x75, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0}; // tounicode
36 
37 static const char16_t C_PASSFAIL[] = {0x70, 0x61, 0x73, 0x73, 0x66, 0x61, 0x69, 0x6C, 0}; // passfail
38 static const char16_t C_PASS[] = {0x70, 0x61, 0x73, 0x73, 0}; // pass
39 static const char16_t C_FAIL[] = {0x66, 0x61, 0x69, 0x6C, 0}; // fail
40 
41 static const char16_t C_DESC[] = {0x64, 0x65, 0x73, 0x63, 0}; // desc
42 static const char16_t C_USESTD3ASCIIRULES[] = {0x55, 0x73, 0x65, 0x53, 0x54, 0x44,
43        0x33, 0x41, 0x53, 0x43, 0x49, 0x49, 0x52, 0x75, 0x6C, 0x65, 0x73, 0}; // UseSTD3ASCIIRules
44 
IdnaConfTest()45 IdnaConfTest::IdnaConfTest(){
46     base = nullptr;
47     len = 0;
48     curOffset = 0;
49 
50     type = option = passfail = -1;
51     namebase.setToBogus();
52     namezone.setToBogus();
53 }
~IdnaConfTest()54 IdnaConfTest::~IdnaConfTest(){
55     delete [] base;
56 }
57 
58 #if !UCONFIG_NO_IDNA
59 
isNewlineMark()60 int IdnaConfTest::isNewlineMark(){
61     static const char16_t LF        = 0x0a;
62     static const char16_t CR        = 0x0d;
63     char16_t 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  */
ReadOneLine(UnicodeString & buf)84 UBool IdnaConfTest::ReadOneLine(UnicodeString& buf){
85     if ( !(curOffset < len) ) return false; // stream end
86 
87     static const char16_t 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         char16_t 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  */
ExplainCodePointTag(UnicodeString & buf)118 void IdnaConfTest::ExplainCodePointTag(UnicodeString& buf){
119     buf.append(static_cast<char16_t>(0)); // add a terminal NUL
120     char16_t* bufBase = buf.getBuffer(buf.length());
121     char16_t* 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 
Call()151 void 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         char16_t result[200] = {0,};   // simple life
157         const char16_t *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, nullptr, &status);
162         } else if (type == 0 && option == 1){
163             uidna_IDNToASCII(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, nullptr, &status);
164         } else if (type == 1 && option == 0){
165             uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_USE_STD3_RULES, nullptr, &status);
166         } else if (type == 1 && option == 1){
167             uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, nullptr, &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 }
205 
Test()206 void IdnaConfTest::Test(){
207     UErrorCode  status  = U_ZERO_ERROR;
208     //
209     //  Open and read the test data file.
210     //
211     const char *testDataDirectory = IntlTest::getSourceTestData(status);
212     CharString testFileName(testDataDirectory, -1, status);
213     testFileName.append("idna_conf.txt", -1, status);
214 
215     base = ReadAndConvertFile(testFileName.data(), len, "UTF-8", status);
216     if (U_FAILURE(status)) {
217         return;
218     }
219 
220     UnicodeString s;
221     UnicodeString key;
222     UnicodeString value;
223 
224     // skip everything before the first "=====" and "=====" itself
225     do {
226         if (!ReadOneLine(s)) {
227             errln("End of file prematurely found");
228             break;
229         }
230     }
231     while (s.compare(C_TAG, -1) != 0);   //"====="
232 
233     while(ReadOneLine(s)){
234         s.trim();
235         key.remove();
236         value.remove();
237         if (s.compare(C_TAG, -1) == 0){   //"====="
238             Call();
239        } else {
240             // explain      key:value
241             int p = s.indexOf(static_cast<char16_t>(0x3A)); // :
242             key.setTo(s,0,p).trim();
243             value.setTo(s,p+1).trim();
244             if (key.compare(C_TYPE, -1) == 0){
245                 if (value.compare(C_TOASCII, -1) == 0) {
246                     type = 0;
247                 } else if (value.compare(C_TOUNICODE, -1) == 0){
248                     type = 1;
249                 }
250             } else if (key.compare(C_PASSFAIL, -1) == 0){
251                 if (value.compare(C_PASS, -1) == 0){
252                     passfail = 0;
253                 } else if (value.compare(C_FAIL, -1) == 0){
254                     passfail = 1;
255                 }
256             } else if (key.compare(C_DESC, -1) == 0){
257                 if (value.indexOf(C_USESTD3ASCIIRULES, u_strlen(C_USESTD3ASCIIRULES), 0) == -1){
258                     option = 1; // not found
259                 } else {
260                     option = 0;
261                 }
262                 id.setTo(value, 0, value.indexOf(static_cast<char16_t>(0x20))); // space
263             } else if (key.compare(C_NAMEZONE, -1) == 0){
264                 ExplainCodePointTag(value);
265                 namezone.setTo(value);
266             } else if (key.compare(C_NAMEBASE, -1) == 0){
267                 ExplainCodePointTag(value);
268                 namebase.setTo(value);
269             }
270             // just skip other lines
271         }
272     }
273 
274     Call(); // for last record
275 }
276 #else
Test()277 void IdnaConfTest::Test()
278 {
279   // test nothing...
280 }
281 #endif
282 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)283 void IdnaConfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/){
284     switch (index) {
285         TESTCASE(0,Test);
286         default: name = ""; break;
287     }
288 }
289 
290 #endif
291