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