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 UChar C_TAG[] = {0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0}; // =====
30 static const UChar C_NAMEZONE[] = {0x6E, 0x61, 0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0}; // namezone
31 static const UChar C_NAMEBASE[] = {0x6E, 0x61, 0x6D, 0x65, 0x62, 0x61, 0x73, 0x65, 0}; // namebase
32
33 static const UChar C_TYPE[] = {0x74, 0x79, 0x70, 0x65, 0}; // type
34 static const UChar C_TOASCII[] = {0x74, 0x6F, 0x61, 0x73, 0x63, 0x69, 0x69, 0}; // toascii
35 static const UChar C_TOUNICODE[] = {0x74, 0x6F, 0x75, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0}; // tounicode
36
37 static const UChar C_PASSFAIL[] = {0x70, 0x61, 0x73, 0x73, 0x66, 0x61, 0x69, 0x6C, 0}; // passfail
38 static const UChar C_PASS[] = {0x70, 0x61, 0x73, 0x73, 0}; // pass
39 static const UChar C_FAIL[] = {0x66, 0x61, 0x69, 0x6C, 0}; // fail
40
41 static const UChar C_DESC[] = {0x64, 0x65, 0x73, 0x63, 0}; // desc
42 static 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
IdnaConfTest()45 IdnaConfTest::IdnaConfTest(){
46 base = NULL;
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 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 */
ReadOneLine(UnicodeString & buf)84 UBool 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 */
ExplainCodePointTag(UnicodeString & buf)118 void 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
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 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
Test(void)207 void 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
Test(void)278 void IdnaConfTest::Test(void)
279 {
280 // test nothing...
281 }
282 #endif
283
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)284 void 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