1 2 #define WIN32_NO_STATUS 3 #include <wine/unicode.h> 4 5 #define NDEBUG 6 #include <debug.h> 7 8 /* Taken from Wine kernel32/locale.c */ 9 10 enum { 11 BASE = 36, 12 TMIN = 1, 13 TMAX = 26, 14 SKEW = 38, 15 DAMP = 700, 16 INIT_BIAS = 72, 17 INIT_N = 128 18 }; 19 20 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime) 21 { 22 INT k; 23 24 delta /= (firsttime ? DAMP : 2); 25 delta += delta/numpoints; 26 27 for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE) 28 delta /= BASE-TMIN; 29 return k+((BASE-TMIN+1)*delta)/(delta+SKEW); 30 } 31 32 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch ) 33 { 34 return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)]; 35 } 36 37 INT WINAPI IdnToNameprepUnicode(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar, 38 LPWSTR lpNameprepCharStr, INT cchNameprepChar) 39 { 40 enum { 41 UNASSIGNED = 0x1, 42 PROHIBITED = 0x2, 43 BIDI_RAL = 0x4, 44 BIDI_L = 0x8 45 }; 46 47 extern const unsigned short nameprep_char_type[]; 48 extern const WCHAR nameprep_mapping[]; 49 const WCHAR *ptr; 50 WORD flags; 51 WCHAR buf[64], *map_str, norm_str[64], ch; 52 DWORD i, map_len, norm_len, mask, label_start, label_end, out = 0; 53 BOOL have_bidi_ral, prohibit_bidi_ral, ascii_only; 54 55 DPRINT("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar, 56 lpNameprepCharStr, cchNameprepChar); 57 58 if(dwFlags & ~(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES)) { 59 SetLastError(ERROR_INVALID_FLAGS); 60 return 0; 61 } 62 63 if(!lpUnicodeCharStr || cchUnicodeChar<-1) { 64 SetLastError(ERROR_INVALID_PARAMETER); 65 return 0; 66 } 67 68 if(cchUnicodeChar == -1) 69 cchUnicodeChar = (UINT)strlenW(lpUnicodeCharStr)+1; 70 if(!cchUnicodeChar || (cchUnicodeChar==1 && lpUnicodeCharStr[0]==0)) { 71 SetLastError(ERROR_INVALID_NAME); 72 return 0; 73 } 74 75 for(label_start=0; label_start<(UINT)cchUnicodeChar;) { 76 ascii_only = TRUE; 77 for(i=label_start; i<(UINT)cchUnicodeChar; i++) { 78 ch = lpUnicodeCharStr[i]; 79 80 if(i!=cchUnicodeChar-1 && !ch) { 81 SetLastError(ERROR_INVALID_NAME); 82 return 0; 83 } 84 /* check if ch is one of label separators defined in RFC3490 */ 85 if(!ch || ch=='.' || ch==0x3002 || ch==0xff0e || ch==0xff61) 86 break; 87 88 if(ch > 0x7f) { 89 ascii_only = FALSE; 90 continue; 91 } 92 93 if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0) 94 continue; 95 if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z') 96 || (ch>='0' && ch<='9') || ch=='-') 97 continue; 98 99 SetLastError(ERROR_INVALID_NAME); 100 return 0; 101 } 102 label_end = i; 103 /* last label may be empty */ 104 if(label_start==label_end && ch) { 105 SetLastError(ERROR_INVALID_NAME); 106 return 0; 107 } 108 109 if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpUnicodeCharStr[label_start]=='-' || 110 lpUnicodeCharStr[label_end-1]=='-')) { 111 SetLastError(ERROR_INVALID_NAME); 112 return 0; 113 } 114 115 if(ascii_only) { 116 /* maximal label length is 63 characters */ 117 if(label_end-label_start > 63) { 118 SetLastError(ERROR_INVALID_NAME); 119 return 0; 120 } 121 if(label_end < (UINT)cchUnicodeChar) 122 label_end++; 123 124 if(!lpNameprepCharStr) { 125 out += label_end-label_start; 126 }else if(out+label_end-label_start <= (UINT)cchNameprepChar) { 127 memcpy(lpNameprepCharStr+out, lpUnicodeCharStr+label_start, 128 (label_end-label_start)*sizeof(WCHAR)); 129 if(lpUnicodeCharStr[label_end-1] > 0x7f) 130 lpNameprepCharStr[out+label_end-label_start-1] = '.'; 131 out += label_end-label_start; 132 }else { 133 SetLastError(ERROR_INSUFFICIENT_BUFFER); 134 return 0; 135 } 136 137 label_start = label_end; 138 continue; 139 } 140 141 map_len = 0; 142 for(i=label_start; i<label_end; i++) { 143 ch = lpUnicodeCharStr[i]; 144 ptr = nameprep_mapping + nameprep_mapping[ch>>8]; 145 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f); 146 147 if(!ptr[0]) map_len++; 148 else if(!ptr[1]) map_len++; 149 else if(!ptr[2]) map_len += 2; 150 else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) map_len += 3; 151 } 152 if(map_len*sizeof(WCHAR) > sizeof(buf)) { 153 map_str = HeapAlloc(GetProcessHeap(), 0, map_len*sizeof(WCHAR)); 154 if(!map_str) { 155 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 156 return 0; 157 } 158 }else { 159 map_str = buf; 160 } 161 map_len = 0; 162 for(i=label_start; i<label_end; i++) { 163 ch = lpUnicodeCharStr[i]; 164 ptr = nameprep_mapping + nameprep_mapping[ch>>8]; 165 ptr = nameprep_mapping + ptr[(ch>>4)&0x0f] + 3*(ch&0x0f); 166 167 if(!ptr[0]) { 168 map_str[map_len++] = ch; 169 }else if(!ptr[1]) { 170 map_str[map_len++] = ptr[0]; 171 }else if(!ptr[2]) { 172 map_str[map_len++] = ptr[0]; 173 map_str[map_len++] = ptr[1]; 174 }else if(ptr[0]!=0xffff || ptr[1]!=0xffff || ptr[2]!=0xffff) { 175 map_str[map_len++] = ptr[0]; 176 map_str[map_len++] = ptr[1]; 177 map_str[map_len++] = ptr[2]; 178 } 179 } 180 181 norm_len = FoldStringW(MAP_FOLDCZONE, map_str, map_len, 182 norm_str, sizeof(norm_str)/sizeof(WCHAR)-1); 183 if(map_str != buf) 184 HeapFree(GetProcessHeap(), 0, map_str); 185 if(!norm_len) { 186 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) 187 SetLastError(ERROR_INVALID_NAME); 188 return 0; 189 } 190 191 if(label_end < (UINT)cchUnicodeChar) { 192 norm_str[norm_len++] = lpUnicodeCharStr[label_end] ? '.' : 0; 193 label_end++; 194 } 195 196 if(!lpNameprepCharStr) { 197 out += norm_len; 198 }else if(out+norm_len <= (UINT)cchNameprepChar) { 199 memcpy(lpNameprepCharStr+out, norm_str, norm_len*sizeof(WCHAR)); 200 out += norm_len; 201 }else { 202 SetLastError(ERROR_INSUFFICIENT_BUFFER); 203 return 0; 204 } 205 206 have_bidi_ral = prohibit_bidi_ral = FALSE; 207 mask = PROHIBITED; 208 if((dwFlags&IDN_ALLOW_UNASSIGNED) == 0) 209 mask |= UNASSIGNED; 210 for(i=0; i<norm_len; i++) { 211 ch = norm_str[i]; 212 flags = get_table_entry( nameprep_char_type, ch ); 213 214 if(flags & mask) { 215 SetLastError((flags & PROHIBITED) ? ERROR_INVALID_NAME 216 : ERROR_NO_UNICODE_TRANSLATION); 217 return 0; 218 } 219 220 if(flags & BIDI_RAL) 221 have_bidi_ral = TRUE; 222 if(flags & BIDI_L) 223 prohibit_bidi_ral = TRUE; 224 } 225 226 if(have_bidi_ral) { 227 ch = norm_str[0]; 228 flags = get_table_entry( nameprep_char_type, ch ); 229 if((flags & BIDI_RAL) == 0) 230 prohibit_bidi_ral = TRUE; 231 232 ch = norm_str[norm_len-1]; 233 flags = get_table_entry( nameprep_char_type, ch ); 234 if((flags & BIDI_RAL) == 0) 235 prohibit_bidi_ral = TRUE; 236 } 237 238 if(have_bidi_ral && prohibit_bidi_ral) { 239 SetLastError(ERROR_INVALID_NAME); 240 return 0; 241 } 242 243 label_start = label_end; 244 } 245 246 return out; 247 } 248 249 INT WINAPI IdnToAscii(DWORD dwFlags, LPCWSTR lpUnicodeCharStr, INT cchUnicodeChar, 250 LPWSTR lpASCIICharStr, INT cchASCIIChar) 251 { 252 static const WCHAR prefixW[] = {'x','n','-','-'}; 253 254 WCHAR *norm_str; 255 INT i, label_start, label_end, norm_len, out_label, out = 0; 256 257 DPRINT("%x %p %d %p %d\n", dwFlags, lpUnicodeCharStr, cchUnicodeChar, 258 lpASCIICharStr, cchASCIIChar); 259 260 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, cchUnicodeChar, NULL, 0); 261 if(!norm_len) 262 return 0; 263 norm_str = HeapAlloc(GetProcessHeap(), 0, norm_len*sizeof(WCHAR)); 264 if(!norm_str) { 265 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 266 return 0; 267 } 268 norm_len = IdnToNameprepUnicode(dwFlags, lpUnicodeCharStr, 269 cchUnicodeChar, norm_str, norm_len); 270 if(!norm_len) { 271 HeapFree(GetProcessHeap(), 0, norm_str); 272 return 0; 273 } 274 275 for(label_start=0; label_start<norm_len;) { 276 INT n = INIT_N, bias = INIT_BIAS; 277 INT delta = 0, b = 0, h; 278 279 out_label = out; 280 for(i=label_start; i<norm_len && norm_str[i]!='.' && 281 norm_str[i]!=0x3002 && norm_str[i]!='\0'; i++) 282 if(norm_str[i] < 0x80) 283 b++; 284 label_end = i; 285 286 if(b == label_end-label_start) { 287 if(label_end < norm_len) 288 b++; 289 if(!lpASCIICharStr) { 290 out += b; 291 }else if(out+b <= cchASCIIChar) { 292 memcpy(lpASCIICharStr+out, norm_str+label_start, b*sizeof(WCHAR)); 293 out += b; 294 }else { 295 HeapFree(GetProcessHeap(), 0, norm_str); 296 SetLastError(ERROR_INSUFFICIENT_BUFFER); 297 return 0; 298 } 299 label_start = label_end+1; 300 continue; 301 } 302 303 if(!lpASCIICharStr) { 304 out += 5+b; /* strlen(xn--...-) */ 305 }else if(out+5+b <= cchASCIIChar) { 306 memcpy(lpASCIICharStr+out, prefixW, sizeof(prefixW)); 307 out += 4; 308 for(i=label_start; i<label_end; i++) 309 if(norm_str[i] < 0x80) 310 lpASCIICharStr[out++] = norm_str[i]; 311 lpASCIICharStr[out++] = '-'; 312 }else { 313 HeapFree(GetProcessHeap(), 0, norm_str); 314 SetLastError(ERROR_INSUFFICIENT_BUFFER); 315 return 0; 316 } 317 if(!b) 318 out--; 319 320 for(h=b; h<label_end-label_start;) { 321 INT m = 0xffff, q, k; 322 323 for(i=label_start; i<label_end; i++) { 324 if(norm_str[i]>=n && m>norm_str[i]) 325 m = norm_str[i]; 326 } 327 delta += (m-n)*(h+1); 328 n = m; 329 330 for(i=label_start; i<label_end; i++) { 331 if(norm_str[i] < n) { 332 delta++; 333 }else if(norm_str[i] == n) { 334 for(q=delta, k=BASE; ; k+=BASE) { 335 INT t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias; 336 INT disp = q<t ? q : t+(q-t)%(BASE-t); 337 if(!lpASCIICharStr) { 338 out++; 339 }else if(out+1 <= cchASCIIChar) { 340 lpASCIICharStr[out++] = disp<='z'-'a' ? 341 'a'+disp : '0'+disp-'z'+'a'-1; 342 }else { 343 HeapFree(GetProcessHeap(), 0, norm_str); 344 SetLastError(ERROR_INSUFFICIENT_BUFFER); 345 return 0; 346 } 347 if(q < t) 348 break; 349 q = (q-t)/(BASE-t); 350 } 351 bias = adapt(delta, h+1, h==b); 352 delta = 0; 353 h++; 354 } 355 } 356 delta++; 357 n++; 358 } 359 360 if(out-out_label > 63) { 361 HeapFree(GetProcessHeap(), 0, norm_str); 362 SetLastError(ERROR_INVALID_NAME); 363 return 0; 364 } 365 366 if(label_end < norm_len) { 367 if(!lpASCIICharStr) { 368 out++; 369 }else if(out+1 <= cchASCIIChar) { 370 lpASCIICharStr[out++] = norm_str[label_end] ? '.' : 0; 371 }else { 372 HeapFree(GetProcessHeap(), 0, norm_str); 373 SetLastError(ERROR_INSUFFICIENT_BUFFER); 374 return 0; 375 } 376 } 377 label_start = label_end+1; 378 } 379 380 HeapFree(GetProcessHeap(), 0, norm_str); 381 return out; 382 } 383