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