xref: /reactos/dll/nls/normaliz/IdnToAscii.c (revision 4561998a)
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