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