| /* | 
 |  ******************************************************************************* | 
 |  * | 
 |  *   Copyright (C) 2005-2014, International Business Machines | 
 |  *   Corporation and others.  All Rights Reserved. | 
 |  * | 
 |  ******************************************************************************* | 
 |  * | 
 |  *   created on: 2005jun15 | 
 |  *   created by: Raymond Yang | 
 |  */ | 
 |  | 
 | #if !UCONFIG_NO_IDNA  | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include "unicode/utypes.h" | 
 | #include "unicode/ucnv.h" | 
 | #include "unicode/ustring.h" | 
 | #include "unicode/uidna.h" | 
 |  | 
 | #include "idnaconf.h" | 
 |  | 
 | static const UChar C_TAG[] = {0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0}; // ===== | 
 | static const UChar C_NAMEZONE[] = {0x6E, 0x61, 0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0}; // namezone  | 
 | static const UChar C_NAMEBASE[] = {0x6E, 0x61, 0x6D, 0x65, 0x62, 0x61, 0x73, 0x65, 0}; // namebase  | 
 |  | 
 | static const UChar C_TYPE[] = {0x74, 0x79, 0x70, 0x65, 0}; // type | 
 | static const UChar C_TOASCII[]  =  {0x74, 0x6F, 0x61, 0x73, 0x63, 0x69, 0x69, 0};       // toascii | 
 | static const UChar C_TOUNICODE[] = {0x74, 0x6F, 0x75, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0}; // tounicode | 
 |  | 
 | static const UChar C_PASSFAIL[] = {0x70, 0x61, 0x73, 0x73, 0x66, 0x61, 0x69, 0x6C, 0}; // passfail | 
 | static const UChar C_PASS[] = {0x70, 0x61, 0x73, 0x73, 0}; // pass | 
 | static const UChar C_FAIL[] = {0x66, 0x61, 0x69, 0x6C, 0}; // fail | 
 |  | 
 | static const UChar C_DESC[] = {0x64, 0x65, 0x73, 0x63, 0}; // desc | 
 | static const UChar C_USESTD3ASCIIRULES[] = {0x55, 0x73, 0x65, 0x53, 0x54, 0x44,  | 
 |        0x33, 0x41, 0x53, 0x43, 0x49, 0x49, 0x52, 0x75, 0x6C, 0x65, 0x73, 0}; // UseSTD3ASCIIRules | 
 |  | 
 | IdnaConfTest::IdnaConfTest(){ | 
 |     base = NULL; | 
 |     len = 0; | 
 |     curOffset = 0; | 
 |  | 
 |     type = option = passfail = -1; | 
 |     namebase.setToBogus(); | 
 |     namezone.setToBogus(); | 
 | } | 
 | IdnaConfTest::~IdnaConfTest(){ | 
 |     delete [] base; | 
 | } | 
 |  | 
 | #if !UCONFIG_NO_IDNA | 
 | /* this function is modified from RBBITest::ReadAndConvertFile()  | 
 |  * | 
 |  */ | 
 | UBool IdnaConfTest::ReadAndConvertFile(){ | 
 |      | 
 |     char * source = NULL; | 
 |     size_t source_len; | 
 |  | 
 |     // read the test data file to memory | 
 |     FILE* f    = NULL; | 
 |     UErrorCode  status  = U_ZERO_ERROR; | 
 |  | 
 |     const char *path = IntlTest::getSourceTestData(status); | 
 |     if (U_FAILURE(status)) { | 
 |         errln("%s", u_errorName(status)); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     const char* name = "idna_conf.txt";     // test data file | 
 |     int t = strlen(path) + strlen(name) + 1; | 
 |     char* absolute_name = new char[t]; | 
 |     strcpy(absolute_name, path); | 
 |     strcat(absolute_name, name); | 
 |     f = fopen(absolute_name, "rb"); | 
 |     delete [] absolute_name; | 
 |  | 
 |     if (f == NULL){ | 
 |         dataerrln("fopen error on %s", name); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     fseek( f, 0, SEEK_END); | 
 |     if ((source_len = ftell(f)) <= 0){ | 
 |         errln("Error reading test data file."); | 
 |         fclose(f); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     source = new char[source_len]; | 
 |     fseek(f, 0, SEEK_SET); | 
 |     if (fread(source, 1, source_len, f) != source_len) { | 
 |         errln("Error reading test data file."); | 
 |         delete [] source; | 
 |         fclose(f); | 
 |         return FALSE; | 
 |     } | 
 |     fclose(f); | 
 |  | 
 |     // convert the UTF-8 encoded stream to UTF-16 stream | 
 |     UConverter* conv = ucnv_open("utf-8", &status); | 
 |     int dest_len = ucnv_toUChars(conv, | 
 |                                 NULL,           //  dest, | 
 |                                 0,              //  destCapacity, | 
 |                                 source, | 
 |                                 source_len, | 
 |                                 &status); | 
 |     if (status == U_BUFFER_OVERFLOW_ERROR) { | 
 |         // Buffer Overflow is expected from the preflight operation. | 
 |         status = U_ZERO_ERROR; | 
 |         UChar * dest = NULL; | 
 |         dest = new UChar[ dest_len + 1]; | 
 |         ucnv_toUChars(conv, dest, dest_len + 1, source, source_len, &status); | 
 |         // Do not know the "if possible" behavior of ucnv_toUChars() | 
 |         // Do it by ourself. | 
 |         dest[dest_len] = 0;  | 
 |         len = dest_len; | 
 |         base = dest; | 
 |         delete [] source; | 
 |         ucnv_close(conv); | 
 |         return TRUE;    // The buffer will owned by caller. | 
 |     } | 
 |     errln("UConverter error: %s", u_errorName(status)); | 
 |     delete [] source; | 
 |     ucnv_close(conv); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | int IdnaConfTest::isNewlineMark(){ | 
 |     static const UChar LF        = 0x0a; | 
 |     static const UChar CR        = 0x0d; | 
 |     UChar c = base[curOffset]; | 
 |     // CR LF | 
 |     if ( c == CR && curOffset + 1 < len && base[curOffset + 1] == LF){ | 
 |         return 2; | 
 |     } | 
 |  | 
 |     // CR or LF | 
 |     if ( c == CR || c == LF) { | 
 |         return 1; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Read a logical line. | 
 |  * | 
 |  * All lines ending in a backslash (\) and immediately followed by a newline  | 
 |  * character are joined with the next line in the source file forming logical | 
 |  * lines from the physical lines. | 
 |  * | 
 |  */ | 
 | UBool IdnaConfTest::ReadOneLine(UnicodeString& buf){ | 
 |     if ( !(curOffset < len) ) return FALSE; // stream end | 
 |  | 
 |     static const UChar BACKSLASH = 0x5c; | 
 |     buf.remove(); | 
 |     int t = 0; | 
 |     while (curOffset < len){ | 
 |         if ((t = isNewlineMark())) {  // end of line | 
 |             curOffset += t; | 
 |             break; | 
 |         } | 
 |         UChar c = base[curOffset]; | 
 |         if (c == BACKSLASH && curOffset < len -1){  // escaped new line mark | 
 |             if ((t = isNewlineMark())){ | 
 |                 curOffset += 1 + t;  // BACKSLAH and NewlineMark | 
 |                 continue; | 
 |             } | 
 |         }; | 
 |         buf.append(c); | 
 |         curOffset++; | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 | // | 
 | //=============================================================== | 
 | // | 
 |  | 
 | /* Explain <xxxxx> tag to a native value | 
 |  * | 
 |  * Since <xxxxx> is always larger than the native value, | 
 |  * the operation will replace the tag directly in the buffer, | 
 |  * and, of course, will shift tail elements. | 
 |  */ | 
 | void IdnaConfTest::ExplainCodePointTag(UnicodeString& buf){ | 
 |     buf.append((UChar)0);    // add a terminal NULL | 
 |     UChar* bufBase = buf.getBuffer(buf.length()); | 
 |     UChar* p = bufBase; | 
 |     while (*p != 0){ | 
 |         if ( *p != 0x3C){    // < | 
 |             *bufBase++ = *p++; | 
 |         } else { | 
 |             p++;    // skip < | 
 |             UChar32 cp = 0; | 
 |             for ( ;*p != 0x3E; p++){   // > | 
 |                 if (0x30 <= *p && *p <= 0x39){        // 0-9 | 
 |                     cp = (cp * 16) + (*p - 0x30); | 
 |                 } else if (0x61 <= *p && *p <= 0x66){ // a-f | 
 |                     cp = (cp * 16) + (*p - 0x61) + 10; | 
 |                 } else if (0x41 <= *p && *p <= 0x46) {// A-F | 
 |                     cp = (cp * 16) + (*p - 0x41) + 10; | 
 |                 } | 
 |                 // no else. hope everything is good. | 
 |             } | 
 |             p++;    // skip > | 
 |             if (U_IS_BMP(cp)){ | 
 |                 *bufBase++ = cp; | 
 |             } else { | 
 |                 *bufBase++ = U16_LEAD(cp); | 
 |                 *bufBase++ = U16_TRAIL(cp); | 
 |             } | 
 |         } | 
 |     } | 
 |     *bufBase = 0;  // close our buffer | 
 |     buf.releaseBuffer(); | 
 | } | 
 |  | 
 | void IdnaConfTest::Call(){ | 
 |     if (type == -1 || option == -1 || passfail == -1 || namebase.isBogus() || namezone.isBogus()){ | 
 |         errln("Incomplete record"); | 
 |     } else { | 
 |         UErrorCode status = U_ZERO_ERROR; | 
 |         UChar result[200] = {0,};   // simple life | 
 |         const UChar *p = namebase.getTerminatedBuffer(); | 
 |         const int p_len = namebase.length(); | 
 |  | 
 |         if (type == 0 && option == 0){ | 
 |             uidna_IDNToASCII(p, p_len, result, 200, UIDNA_USE_STD3_RULES, NULL, &status); | 
 |         } else if (type == 0 && option == 1){ | 
 |             uidna_IDNToASCII(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, NULL, &status); | 
 |         } else if (type == 1 && option == 0){ | 
 |             uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_USE_STD3_RULES, NULL, &status); | 
 |         } else if (type == 1 && option == 1){ | 
 |             uidna_IDNToUnicode(p, p_len, result, 200, UIDNA_ALLOW_UNASSIGNED, NULL, &status); | 
 |         } | 
 |         if (passfail == 0){ | 
 |             if (U_FAILURE(status)){ | 
 |                 id.append(" should pass, but failed. - "); | 
 |                 id.append(u_errorName(status)); | 
 |                 errcheckln(status, id); | 
 |             } else{ | 
 |                 if (namezone.compare(result, -1) == 0){ | 
 |                     // expected | 
 |                     logln(UnicodeString("namebase: ") + prettify(namebase) + UnicodeString(" result: ") + prettify(result)); | 
 |                 } else { | 
 |                     id.append(" no error, but result is not as expected."); | 
 |                     errln(id); | 
 |                 } | 
 |             } | 
 |         } else if (passfail == 1){ | 
 |             if (U_FAILURE(status)){ | 
 |                 // expected | 
 |                 // TODO: Uncomment this when U_IDNA_ZERO_LENGTH_LABEL_ERROR is added to u_errorName | 
 |                 //logln("Got the expected error: " + UnicodeString(u_errorName(status))); | 
 |             } else{ | 
 |                 if (namebase.compare(result, -1) == 0){ | 
 |                     // garbage in -> garbage out | 
 |                     logln(UnicodeString("ICU will not recognize malformed ACE-Prefixes or incorrect ACE-Prefixes. ") + UnicodeString("namebase: ") + prettify(namebase) + UnicodeString(" result: ") + prettify(result)); | 
 |                 } else { | 
 |                     id.append(" should fail, but not failed. "); | 
 |                     id.append(u_errorName(status)); | 
 |                     errln(id); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |     type = option = passfail = -1; | 
 |     namebase.setToBogus(); | 
 |     namezone.setToBogus(); | 
 |     id.remove(); | 
 |     return; | 
 | } | 
 |  | 
 | void IdnaConfTest::Test(void){ | 
 |     if (!ReadAndConvertFile())return; | 
 |  | 
 |     UnicodeString s; | 
 |     UnicodeString key; | 
 |     UnicodeString value; | 
 |  | 
 |     // skip everything before the first "=====" and "=====" itself | 
 |     do { | 
 |         if (!ReadOneLine(s)) { | 
 |             errln("End of file prematurely found"); | 
 |             break; | 
 |         } | 
 |     } | 
 |     while (s.compare(C_TAG, -1) != 0);   //"=====" | 
 |  | 
 |     while(ReadOneLine(s)){ | 
 |         s.trim(); | 
 |         key.remove(); | 
 |         value.remove(); | 
 |         if (s.compare(C_TAG, -1) == 0){   //"=====" | 
 |             Call(); | 
 |        } else { | 
 |             // explain      key:value | 
 |             int p = s.indexOf((UChar)0x3A);    // : | 
 |             key.setTo(s,0,p).trim(); | 
 |             value.setTo(s,p+1).trim(); | 
 |             if (key.compare(C_TYPE, -1) == 0){ | 
 |                 if (value.compare(C_TOASCII, -1) == 0) { | 
 |                     type = 0; | 
 |                 } else if (value.compare(C_TOUNICODE, -1) == 0){ | 
 |                     type = 1; | 
 |                 } | 
 |             } else if (key.compare(C_PASSFAIL, -1) == 0){ | 
 |                 if (value.compare(C_PASS, -1) == 0){ | 
 |                     passfail = 0; | 
 |                 } else if (value.compare(C_FAIL, -1) == 0){ | 
 |                     passfail = 1; | 
 |                 } | 
 |             } else if (key.compare(C_DESC, -1) == 0){ | 
 |                 if (value.indexOf(C_USESTD3ASCIIRULES, u_strlen(C_USESTD3ASCIIRULES), 0) == -1){ | 
 |                     option = 1; // not found | 
 |                 } else { | 
 |                     option = 0; | 
 |                 } | 
 |                 id.setTo(value, 0, value.indexOf((UChar)0x20));    // space | 
 |             } else if (key.compare(C_NAMEZONE, -1) == 0){ | 
 |                 ExplainCodePointTag(value); | 
 |                 namezone.setTo(value); | 
 |             } else if (key.compare(C_NAMEBASE, -1) == 0){ | 
 |                 ExplainCodePointTag(value); | 
 |                 namebase.setTo(value); | 
 |             } | 
 |             // just skip other lines | 
 |         } | 
 |     } | 
 |  | 
 |     Call(); // for last record | 
 | } | 
 | #else | 
 | void IdnaConfTest::Test(void) | 
 | { | 
 |   // test nothing... | 
 | } | 
 | #endif | 
 |  | 
 | void IdnaConfTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/){ | 
 |     switch (index) { | 
 |         TESTCASE(0,Test); | 
 |         default: name = ""; break; | 
 |     } | 
 | } | 
 |  | 
 | #endif |