3cb53b26cf8edd2f30a9f1cce37c08dc5e9ba3e5
[reactos.git] / reactos / dll / win32 / kernel32_vista / IdnToAscii.c
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 }