1#ifndef lint
2static char *rcsid = "$Id: res.c,v 1.1 2003/06/04 00:26:10 marka Exp $";
3#endif
4
5/*
6 * Copyright (c) 2000,2002 Japan Network Information Center.
7 * All rights reserved.
8 *
9 * By using this file, you agree to the terms and conditions set forth bellow.
10 *
11 * 			LICENSE TERMS AND CONDITIONS
12 *
13 * The following License Terms and Conditions apply, unless a different
14 * license is obtained from Japan Network Information Center ("JPNIC"),
15 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
16 * Chiyoda-ku, Tokyo 101-0047, Japan.
17 *
18 * 1. Use, Modification and Redistribution (including distribution of any
19 *    modified or derived work) in source and/or binary forms is permitted
20 *    under this License Terms and Conditions.
21 *
22 * 2. Redistribution of source code must retain the copyright notices as they
23 *    appear in each source code file, this License Terms and Conditions.
24 *
25 * 3. Redistribution in binary form must reproduce the Copyright Notice,
26 *    this License Terms and Conditions, in the documentation and/or other
27 *    materials provided with the distribution.  For the purposes of binary
28 *    distribution the "Copyright Notice" refers to the following language:
29 *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
30 *
31 * 4. The name of JPNIC may not be used to endorse or promote products
32 *    derived from this Software without specific prior written approval of
33 *    JPNIC.
34 *
35 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
36 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
38 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
39 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
43 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
44 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
45 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
46 */
47
48#include <config.h>
49
50#include <stddef.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include <idn/result.h>
55#include <idn/assert.h>
56#include <idn/logmacro.h>
57#include <idn/converter.h>
58#include <idn/normalizer.h>
59#include <idn/checker.h>
60#include <idn/mapper.h>
61#include <idn/mapselector.h>
62#include <idn/delimitermap.h>
63#include <idn/resconf.h>
64#include <idn/res.h>
65#include <idn/util.h>
66#include <idn/debug.h>
67#include <idn/ucs4.h>
68
69#ifndef IDN_UTF8_ENCODING_NAME
70#define IDN_UTF8_ENCODING_NAME "UTF-8"		/* by IANA */
71#endif
72
73#ifndef WITHOUT_ICONV
74#define ENCODE_MASK \
75	(IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
76	 IDN_NORMALIZE | IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | \
77	 IDN_ASCCHECK | IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | \
78	 IDN_UNDOIFERR)
79#define DECODE_MASK \
80	(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
81	 IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
82	 IDN_RTCHECK | IDN_LOCALCONV | IDN_DECODE_QUERY)
83#else
84#define ENCODE_MASK \
85	(IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | IDN_NORMALIZE | \
86	 IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | IDN_ASCCHECK | \
87	 IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | IDN_UNDOIFERR)
88#define DECODE_MASK \
89	(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
90	 IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
91	 IDN_RTCHECK | IDN_DECODE_QUERY)
92#endif
93
94#define MAX_LABEL_LENGTH	63
95
96/*
97 * label to convert.
98 */
99typedef struct labellist * labellist_t;
100struct labellist {
101	unsigned long *name;
102	size_t name_length;
103	unsigned long *undo_name;
104	labellist_t next;
105	labellist_t previous;
106	int dot_followed;
107};
108
109typedef idn_result_t (*res_insnproc_t)(idn_resconf_t ctx,
110				       labellist_t label);
111
112static void		idn_res_initialize(void);
113static idn_result_t	copy_verbatim(const char *from, char *to,
114				      size_t tolen);
115static idn_result_t	labellist_create(const unsigned long *name,
116					 labellist_t *labelp);
117static void		labellist_destroy(labellist_t label);
118static idn_result_t	labellist_setname(labellist_t label,
119					  const unsigned long *name);
120static const unsigned long *
121			labellist_getname(labellist_t label);
122static const unsigned long *
123			labellist_gettldname(labellist_t label);
124static idn_result_t	labellist_getnamelist(labellist_t label,
125					      unsigned long *name,
126					      size_t label_length);
127static void		labellist_undo(labellist_t label);
128static labellist_t	labellist_tail(labellist_t label);
129static labellist_t	labellist_previous(labellist_t label);
130
131#ifndef WITHOUT_ICONV
132static idn_result_t	label_localdecodecheck(idn_resconf_t ctx,
133					       labellist_t label);
134#endif
135static idn_result_t	label_idnencode_ace(idn_resconf_t ctx,
136					    labellist_t label);
137static idn_result_t	label_idndecode(idn_resconf_t ctx, labellist_t label);
138static idn_result_t	label_localmap(idn_resconf_t ctx, labellist_t label);
139static idn_result_t	label_map(idn_resconf_t ctx, labellist_t label);
140static idn_result_t	label_normalize(idn_resconf_t ctx, labellist_t label);
141static idn_result_t	label_prohcheck(idn_resconf_t ctx, labellist_t label);
142static idn_result_t	label_unascheck(idn_resconf_t ctx, labellist_t label);
143static idn_result_t	label_bidicheck(idn_resconf_t ctx, labellist_t label);
144static idn_result_t	label_asccheck(idn_resconf_t ctx, labellist_t label);
145static idn_result_t	label_lencheck_ace(idn_resconf_t ctx,
146					   labellist_t label);
147static idn_result_t	label_lencheck_nonace(idn_resconf_t ctx,
148					      labellist_t label);
149static idn_result_t	label_rtcheck(idn_resconf_t ctx, idn_action_t actions,
150				      labellist_t label,
151				      const unsigned long *original_name);
152
153static int initialized;
154static int enabled;
155
156void
157idn_res_enable(int on_off) {
158	if (!initialized) {
159		idn_res_initialize();
160	}
161
162	if (on_off == 0) {
163		enabled = 0;
164	} else {
165		enabled = 1;
166	}
167}
168
169static void
170idn_res_initialize(void) {
171	if (!initialized) {
172		char *value = getenv("IDN_DISABLE");
173
174		if (value == NULL) {
175			enabled = 1;
176		} else {
177			enabled = 0;
178		}
179		initialized = 1;
180	}
181}
182
183idn_result_t
184idn_res_encodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
185		    char *to, size_t tolen) {
186	idn_converter_t local_converter = NULL;
187	idn_converter_t idn_converter = NULL;
188	idn_delimitermap_t delimiter_mapper;
189	idn_result_t r;
190	labellist_t labels = NULL, l;
191	unsigned long *buffer = NULL;
192	size_t buffer_length;
193	int from_is_root;
194	int idn_is_ace;
195
196	assert(ctx != NULL && from != NULL && to != NULL);
197
198	TRACE(("idn_res_encodename(actions=%s, from=\"%s\", tolen=%d)\n",
199		idn__res_actionstostring(actions),
200		idn__debug_xstring(from, 50), (int)tolen));
201
202	if (actions & ~ENCODE_MASK) {
203		WARNING(("idn_res_encodename: invalid actions 0x%x\n",
204			 actions));
205		r = idn_invalid_action;
206		goto ret;
207	}
208
209	if (!initialized)
210		idn_res_initialize();
211	if (!enabled || actions == 0) {
212		r = copy_verbatim(from, to, tolen);
213		goto ret;
214	} else if (tolen <= 0) {
215		r = idn_buffer_overflow;
216		goto ret;
217	}
218
219	if (actions & IDN_ENCODE_QUERY) {
220#ifndef WITHOUT_ICONV
221		actions |= (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | \
222			    IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
223			    IDN_BIDICHECK | IDN_IDNCONV | IDN_LENCHECK);
224#else
225		actions |= (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
226			    IDN_NORMALIZE | IDN_PROHCHECK | IDN_BIDICHECK | \
227			    IDN_IDNCONV | IDN_LENCHECK);
228#endif
229	}
230
231	/*
232	 * Convert `from' to UCS4.
233	 */
234	local_converter = idn_resconf_getlocalconverter(ctx);
235#ifndef WITHOUT_ICONV
236	if (local_converter == NULL) {
237		r = idn_invalid_name;
238		goto ret;
239	}
240#endif
241
242	idn_converter = idn_resconf_getidnconverter(ctx);
243	if (idn_converter != NULL &&
244	    idn_converter_isasciicompatible(idn_converter))
245		idn_is_ace = 1;
246	else
247		idn_is_ace = 0;
248
249	buffer_length = tolen * 2;
250
251	for (;;) {
252		void *new_buffer;
253
254		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
255		if (new_buffer == NULL) {
256			r = idn_nomemory;
257			goto ret;
258		}
259		buffer = (unsigned long *)new_buffer;
260
261		if (actions & IDN_LOCALCONV) {
262			r = idn_converter_convtoucs4(local_converter, from,
263						     buffer, buffer_length);
264		} else {
265			r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
266		}
267		if (r == idn_success)
268			break;
269		else if (r != idn_buffer_overflow)
270			goto ret;
271
272		buffer_length *= 2;
273	}
274
275	if (*buffer == '\0') {
276		if (tolen <= 0) {
277			r = idn_buffer_overflow;
278			goto ret;
279		}
280		*to = '\0';
281		r = idn_success;
282		goto ret;
283	}
284
285	/*
286	 * Delimiter map.
287	 */
288	if (actions & IDN_DELIMMAP) {
289		TRACE(("res delimitermap(name=\"%s\")\n",
290		       idn__debug_ucs4xstring(buffer, 50)));
291
292		delimiter_mapper = idn_resconf_getdelimitermap(ctx);
293		if (delimiter_mapper != NULL) {
294			r = idn_delimitermap_map(delimiter_mapper, buffer,
295						 buffer, buffer_length);
296			idn_delimitermap_destroy(delimiter_mapper);
297			if (r != idn_success)
298				goto ret;
299		}
300		TRACE(("res delimitermap(): success (name=\"%s\")\n",
301		       idn__debug_ucs4xstring(buffer, 50)));
302	}
303
304	from_is_root = (buffer[0] == '.' && buffer[1] == '\0');
305
306	/*
307	 * Split the name into a list of labels.
308	 */
309	r = labellist_create(buffer, &labels);
310	if (r != idn_success)
311		goto ret;
312
313	/*
314	 * Perform conversions and tests.
315	 */
316	for (l = labellist_tail(labels); l != NULL;
317	     l = labellist_previous(l)) {
318
319		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
320			if (actions & IDN_LOCALMAP) {
321				r = label_localmap(ctx, l);
322				if (r != idn_success)
323					goto ret;
324			}
325		}
326
327		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
328			if (actions & IDN_MAP) {
329				r = label_map(ctx, l);
330				if (r != idn_success)
331					goto ret;
332			}
333			if (actions & IDN_NORMALIZE) {
334				r = label_normalize(ctx, l);
335				if (r != idn_success)
336					goto ret;
337			}
338			if (actions & IDN_PROHCHECK) {
339				r = label_prohcheck(ctx, l);
340				if (r == idn_prohibited &&
341				    (actions & IDN_UNDOIFERR)) {
342					labellist_undo(l);
343					continue;
344				} else if (r != idn_success) {
345					goto ret;
346				}
347			}
348			if (actions & IDN_UNASCHECK) {
349				r = label_unascheck(ctx, l);
350				if (r == idn_prohibited &&
351				    (actions & IDN_UNDOIFERR)) {
352					labellist_undo(l);
353					continue;
354				} else if (r != idn_success) {
355					goto ret;
356				}
357			}
358			if (actions & IDN_BIDICHECK) {
359				r = label_bidicheck(ctx, l);
360				if (r == idn_prohibited &&
361				    (actions & IDN_UNDOIFERR)) {
362					labellist_undo(l);
363					continue;
364				} else if (r != idn_success) {
365					goto ret;
366				}
367			}
368		}
369
370		if (actions & IDN_ASCCHECK) {
371			r = label_asccheck(ctx, l);
372			if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) {
373				labellist_undo(l);
374				continue;
375			} else if (r != idn_success) {
376				goto ret;
377			}
378		}
379
380		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
381			if ((actions & IDN_IDNCONV) && idn_is_ace) {
382				r = label_idnencode_ace(ctx, l);
383				if (r != idn_success)
384					goto ret;
385			}
386		}
387
388		if (!from_is_root && (actions & IDN_LENCHECK)) {
389			if (idn_is_ace)
390				r = label_lencheck_ace(ctx, l);
391			else
392				r = label_lencheck_nonace(ctx, l);
393			if (r == idn_invalid_length &&
394			    (actions & IDN_UNDOIFERR)) {
395				labellist_undo(l);
396				continue;
397			} else if (r != idn_success) {
398				goto ret;
399			}
400		}
401	}
402
403	/*
404	 * Concat a list of labels to a name.
405	 */
406	for (;;) {
407		void *new_buffer;
408
409		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
410		if (new_buffer == NULL) {
411			r = idn_nomemory;
412			goto ret;
413		}
414		buffer = (unsigned long *)new_buffer;
415
416		r = labellist_getnamelist(labels, buffer, buffer_length);
417		if (r == idn_success)
418			break;
419		else if (r != idn_buffer_overflow)
420			goto ret;
421
422		buffer_length *= 2;
423	}
424
425	if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) {
426		r = idn_converter_convfromucs4(idn_converter, buffer, to,
427					       tolen);
428	} else {
429		r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
430	}
431
432ret:
433	if (r == idn_success) {
434		TRACE(("idn_res_encodename(): success (to=\"%s\")\n",
435		       idn__debug_xstring(to, 50)));
436	} else {
437		TRACE(("idn_res_encodename(): %s\n", idn_result_tostring(r)));
438	}
439	free(buffer);
440	if (local_converter != NULL)
441		idn_converter_destroy(local_converter);
442	if (idn_converter != NULL)
443		idn_converter_destroy(idn_converter);
444	if (labels != NULL)
445		labellist_destroy(labels);
446	return (r);
447}
448
449idn_result_t
450idn_res_decodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
451		    char *to, size_t tolen) {
452	idn_converter_t local_converter = NULL;
453	idn_converter_t idn_converter = NULL;
454	idn_delimitermap_t delimiter_mapper;
455	idn_result_t r;
456	labellist_t labels = NULL, l;
457	unsigned long *buffer = NULL;
458	unsigned long *saved_name = NULL;
459	size_t buffer_length;
460	int idn_is_ace;
461
462	assert(ctx != NULL && from != NULL && to != NULL);
463
464	TRACE(("idn_res_decodename(actions=%s, from=\"%s\", tolen=%d)\n",
465		idn__res_actionstostring(actions),
466		idn__debug_xstring(from, 50), (int)tolen));
467
468	if (actions & ~DECODE_MASK) {
469		WARNING(("idn_res_decodename: invalid actions 0x%x\n",
470			 actions));
471		r = idn_invalid_action;
472		goto ret;
473	}
474
475	if (!initialized)
476		idn_res_initialize();
477	if (!enabled || actions == 0) {
478		r = copy_verbatim(from, to, tolen);
479		goto ret;
480	} else if (tolen <= 0) {
481		r = idn_buffer_overflow;
482		goto ret;
483	}
484
485	if (actions & IDN_DECODE_QUERY) {
486#ifndef WITHOUT_ICONV
487		actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
488			    IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
489			    IDN_RTCHECK | IDN_LOCALCONV);
490#else
491		actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
492			    IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
493			    IDN_RTCHECK);
494#endif
495	}
496
497	/*
498	 * Convert `from' to UCS4.
499	 */
500	local_converter = idn_resconf_getlocalconverter(ctx);
501#ifndef WITHOUT_ICONV
502	if (local_converter == NULL) {
503		r = idn_invalid_name;
504		goto ret;
505	}
506#endif
507
508	idn_converter = idn_resconf_getidnconverter(ctx);
509	if (idn_converter != NULL &&
510	    idn_converter_isasciicompatible(idn_converter))
511		idn_is_ace = 1;
512	else
513		idn_is_ace = 0;
514
515	buffer_length = tolen * 2;
516
517	TRACE(("res idndecode(name=\"%s\")\n", idn__debug_xstring(from, 50)));
518
519	for (;;) {
520		void *new_buffer;
521
522		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
523		if (new_buffer == NULL) {
524			r = idn_nomemory;
525			goto ret;
526		}
527		buffer = (unsigned long *)new_buffer;
528
529		if ((actions & IDN_IDNCONV) &&
530		     idn_converter != NULL && !idn_is_ace) {
531			r = idn_converter_convtoucs4(idn_converter, from,
532						     buffer, buffer_length);
533		} else {
534			r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
535		}
536		if (r == idn_success)
537			break;
538		else if (r != idn_buffer_overflow)
539			goto ret;
540
541		buffer_length *= 2;
542	}
543
544	if (*buffer == '\0') {
545		if (tolen <= 0) {
546			r = idn_buffer_overflow;
547			goto ret;
548		}
549		*to = '\0';
550		r = idn_success;
551		goto ret;
552	}
553
554	/*
555	 * Delimiter map.
556	 */
557	if (actions & IDN_DELIMMAP) {
558		TRACE(("res delimitermap(name=\"%s\")\n",
559		       idn__debug_ucs4xstring(buffer, 50)));
560
561		delimiter_mapper = idn_resconf_getdelimitermap(ctx);
562		if (delimiter_mapper != NULL) {
563			r = idn_delimitermap_map(delimiter_mapper, buffer,
564						 buffer, buffer_length);
565			idn_delimitermap_destroy(delimiter_mapper);
566			if (r != idn_success)
567				goto ret;
568		}
569		TRACE(("res delimitermap(): success (name=\"%s\")\n",
570		       idn__debug_ucs4xstring(buffer, 50)));
571	}
572
573	/*
574	 * Split the name into a list of labels.
575	 */
576	r = labellist_create(buffer, &labels);
577	if (r != idn_success)
578		goto ret;
579
580	/*
581	 * Perform conversions and tests.
582	 */
583	for (l = labellist_tail(labels); l != NULL;
584	     l = labellist_previous(l)) {
585
586		free(saved_name);
587		saved_name = NULL;
588
589		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
590			if (actions & IDN_MAP) {
591				r = label_map(ctx, l);
592				if (r != idn_success)
593					goto ret;
594			}
595			if (actions & IDN_NORMALIZE) {
596				r = label_normalize(ctx, l);
597				if (r != idn_success)
598					goto ret;
599			}
600			if (actions & IDN_PROHCHECK) {
601				r = label_prohcheck(ctx, l);
602				if (r == idn_prohibited) {
603					labellist_undo(l);
604					continue;
605				} else if (r != idn_success) {
606					goto ret;
607				}
608			}
609			if (actions & IDN_UNASCHECK) {
610				r = label_unascheck(ctx, l);
611				if (r == idn_prohibited) {
612					labellist_undo(l);
613					continue;
614				} else if (r != idn_success) {
615					goto ret;
616				}
617			}
618			if (actions & IDN_BIDICHECK) {
619				r = label_bidicheck(ctx, l);
620				if (r == idn_prohibited) {
621					labellist_undo(l);
622					continue;
623				} else if (r != idn_success) {
624					goto ret;
625				}
626			}
627		}
628
629		if ((actions & IDN_IDNCONV) && idn_is_ace) {
630			saved_name = idn_ucs4_strdup(labellist_getname(l));
631			if (saved_name == NULL) {
632				r = idn_nomemory;
633				goto ret;
634			}
635			r = label_idndecode(ctx, l);
636			if (r == idn_invalid_encoding) {
637				labellist_undo(l);
638				continue;
639			} else if (r != idn_success) {
640				goto ret;
641			}
642		}
643		if ((actions & IDN_RTCHECK) && saved_name != NULL) {
644			r = label_rtcheck(ctx, actions, l, saved_name);
645			if (r == idn_invalid_encoding) {
646				labellist_undo(l);
647				continue;
648			} else if (r != idn_success) {
649				goto ret;
650			}
651		}
652
653#ifndef WITHOUT_ICONV
654		if (actions & IDN_LOCALCONV) {
655			r = label_localdecodecheck(ctx, l);
656			if (r != idn_success)
657				goto ret;
658		}
659#endif
660	}
661
662	/*
663	 * Concat a list of labels to a name.
664	 */
665	for (;;) {
666		void *new_buffer;
667
668		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
669		if (new_buffer == NULL) {
670			r = idn_nomemory;
671			goto ret;
672		}
673		buffer = (unsigned long *)new_buffer;
674
675		r = labellist_getnamelist(labels, buffer, buffer_length);
676		if (r == idn_success)
677			break;
678		else if (r != idn_buffer_overflow)
679			goto ret;
680
681		buffer_length *= 2;
682	}
683
684	if (actions & IDN_LOCALCONV) {
685		r = idn_converter_convfromucs4(local_converter, buffer, to,
686					       tolen);
687	} else {
688		r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
689	}
690
691ret:
692	if (r == idn_success) {
693		TRACE(("idn_res_decodename(): success (to=\"%s\")\n",
694		       idn__debug_xstring(to, 50)));
695	} else {
696		TRACE(("idn_res_decodename(): %s\n", idn_result_tostring(r)));
697	}
698	free(saved_name);
699	free(buffer);
700	if (local_converter != NULL)
701		idn_converter_destroy(local_converter);
702	if (idn_converter != NULL)
703		idn_converter_destroy(idn_converter);
704	if (labels != NULL)
705		labellist_destroy(labels);
706	return (r);
707}
708
709idn_result_t
710idn_res_decodename2(idn_resconf_t ctx, idn_action_t actions, const char *from,
711		    char *to, size_t tolen, const char *auxencoding) {
712#ifdef WITHOUT_ICONV
713	return idn_failure;
714
715#else /* WITHOUT_ICONV */
716	idn_result_t r;
717	idn_converter_t aux_converter = NULL;
718	unsigned long *buffer_ucs4 = NULL;
719	char *buffer_utf8 = NULL;
720	size_t buffer_length;
721
722	assert(ctx != NULL && from != NULL && to != NULL);
723
724	TRACE(("idn_res_decodename2(actions=%s, from=\"%s\", tolen=%d, "
725		"auxencoding=\"%s\")\n",
726		idn__res_actionstostring(actions),
727		idn__debug_xstring(from, 50), (int)tolen,
728		(auxencoding != NULL) ? auxencoding : "<null>"));
729
730	if (!initialized)
731		idn_res_initialize();
732	if (!enabled || actions == 0) {
733		r = copy_verbatim(from, to, tolen);
734		goto ret;
735	} else if (tolen <= 0) {
736		r = idn_buffer_overflow;
737		goto ret;
738	}
739
740	if (auxencoding == NULL ||
741	    strcmp(auxencoding, IDN_UTF8_ENCODING_NAME) == 0 ||
742	    strcmp(auxencoding, "UTF-8") == 0) {
743		return idn_res_decodename(ctx, actions, from, to, tolen);
744	}
745
746	/*
747	 * Convert `from' to UCS4.
748	 */
749	r = idn_resconf_setauxidnconvertername(ctx, auxencoding,
750					       IDN_CONVERTER_DELAYEDOPEN);
751	if (r != idn_success) {
752		goto ret;
753	}
754
755	aux_converter = idn_resconf_getauxidnconverter(ctx);
756	if (aux_converter == NULL) {
757		r = idn_failure;
758		goto ret;
759	}
760
761	buffer_length = tolen * 2;
762	for (;;) {
763		void *new_buffer;
764
765		new_buffer = realloc(buffer_ucs4,
766				     sizeof(*buffer_ucs4) * buffer_length);
767		if (new_buffer == NULL) {
768			r = idn_nomemory;
769			goto ret;
770		}
771		buffer_ucs4 = (unsigned long *)new_buffer;
772
773		r = idn_converter_convtoucs4(aux_converter, from,
774					     buffer_ucs4,
775					     buffer_length);
776		if (r == idn_success)
777			break;
778		else if (r != idn_buffer_overflow)
779			goto ret;
780
781		buffer_length *= 2;
782	}
783
784	if (*buffer_ucs4 == '\0') {
785		if (tolen <= 0) {
786			r = idn_buffer_overflow;
787			goto ret;
788		}
789		*to = '\0';
790		r = idn_success;
791		goto ret;
792	}
793
794	/*
795	 * Convert `buffer_ucs4' to UTF-8.
796	 */
797	buffer_length = tolen * 2;
798	for (;;) {
799		void *new_buffer;
800
801		new_buffer = realloc(buffer_utf8,
802				     sizeof(*buffer_utf8) * buffer_length);
803		if (new_buffer == NULL) {
804			r = idn_nomemory;
805			goto ret;
806		}
807		buffer_utf8 = (char *)new_buffer;
808		r = idn_ucs4_ucs4toutf8(buffer_ucs4, buffer_utf8,
809					buffer_length);
810
811		if (r == idn_success)
812			break;
813		else if (r != idn_buffer_overflow)
814			goto ret;
815
816		buffer_length *= 2;
817	}
818
819	if (*buffer_utf8 == '\0') {
820		if (tolen <= 0) {
821			r = idn_buffer_overflow;
822			goto ret;
823		}
824		*to = '\0';
825		r = idn_success;
826		goto ret;
827	}
828
829	r = idn_res_decodename(ctx, actions, buffer_utf8, to, tolen);
830
831ret:
832	if (r == idn_success) {
833		TRACE(("idn_res_decodename2(): success (to=\"%s\")\n",
834		       idn__debug_xstring(to, 50)));
835	} else {
836		TRACE(("idn_res_decodename2(): %s\n", idn_result_tostring(r)));
837	}
838	free(buffer_ucs4);
839	free(buffer_utf8);
840	if (aux_converter != NULL)
841		idn_converter_destroy(aux_converter);
842
843	return (r);
844
845#endif /* WITHOUT_ICONV */
846}
847
848static idn_result_t
849copy_verbatim(const char *from, char *to, size_t tolen) {
850	size_t fromlen = strlen(from);
851
852	if (fromlen + 1 > tolen)
853		return (idn_buffer_overflow);
854	(void)memcpy(to, from, fromlen + 1);
855	return (idn_success);
856}
857
858static idn_result_t
859labellist_create(const unsigned long *name, labellist_t *labelp) {
860	size_t length, malloc_length;
861	labellist_t head_label = NULL;
862	labellist_t tail_label = NULL;
863	labellist_t new_label = NULL;
864	const unsigned long *endp = NULL;
865	idn_result_t r;
866
867	while (*name != '\0') {
868		for (endp = name; *endp != '.' && *endp != '\0'; endp++)
869			;  /* nothing to be done */
870		length = (endp - name) + 1;
871		malloc_length = length + 15;  /* add 15 for margin */
872
873		new_label = (labellist_t)
874			    malloc(sizeof(struct labellist));
875		if (new_label == NULL) {
876			r = idn_nomemory;
877			goto ret;
878		}
879		if (head_label == NULL)
880			head_label = new_label;
881
882		new_label->name = NULL;
883		new_label->undo_name = NULL;
884		new_label->name_length = malloc_length;
885		new_label->next = NULL;
886		new_label->previous = NULL;
887		new_label->dot_followed = (*endp == '.');
888
889		new_label->name = (unsigned long *)
890				  malloc(sizeof(long) * malloc_length);
891		if (new_label->name == NULL) {
892			r = idn_nomemory;
893			goto ret;
894		}
895		memcpy(new_label->name, name, sizeof(long) * length);
896		*(new_label->name + length - 1) = '\0';
897
898		new_label->undo_name = (unsigned long *)
899				       malloc(sizeof(long) * malloc_length);
900		if (new_label->undo_name == NULL) {
901			r = idn_nomemory;
902			goto ret;
903		}
904		memcpy(new_label->undo_name, name, sizeof(long) * length);
905		*(new_label->undo_name + length - 1) = '\0';
906
907		if (tail_label != NULL) {
908			tail_label->next = new_label;
909			new_label->previous = tail_label;
910		}
911		tail_label = new_label;
912
913		if (*endp == '.')
914			name = endp + 1;
915		else
916			name = endp;
917	}
918
919	*labelp = head_label;
920	r = idn_success;
921
922ret:
923	if (r != idn_success) {
924		if (new_label != NULL) {
925			free(new_label->name);
926			free(new_label->undo_name);
927			free(new_label);
928		}
929		if (head_label != NULL)
930			labellist_destroy(head_label);
931	}
932	return (r);
933}
934
935
936static void
937labellist_destroy(labellist_t label) {
938	labellist_t l, l_next;
939
940	for (l = label; l != NULL; l = l_next) {
941		l_next = l->next;
942		free(l->name);
943		free(l->undo_name);
944		free(l);
945	}
946}
947
948static idn_result_t
949labellist_setname(labellist_t label, const unsigned long *name) {
950	unsigned long *new_name;
951	size_t length, new_length;
952
953	length = idn_ucs4_strlen(name) + 1;
954	new_length = length + 15;  /* add 15 for margin */
955
956	if (label->name_length < new_length) {
957		new_name = (unsigned long *)
958			   realloc(label->name, sizeof(long) * new_length);
959		if (new_name == NULL)
960			return (idn_nomemory);
961		label->name = new_name;
962		label->name_length = new_length;
963	}
964	memcpy(label->name, name, sizeof(long) * length);
965
966	return (idn_success);
967}
968
969static const unsigned long *
970labellist_getname(labellist_t label) {
971	return (label->name);
972}
973
974static const unsigned long *
975labellist_gettldname(labellist_t label) {
976	labellist_t l;
977
978	if (label->previous == NULL && label->next == NULL &&
979	    !label->dot_followed)
980		return (idn_mapselector_getnotld());
981
982	for (l = label; l->next != NULL; l = l->next)
983		;  /* nothing to be done */
984
985	return (l->name);
986}
987
988static idn_result_t
989labellist_getnamelist(labellist_t label, unsigned long *name,
990			  size_t name_length) {
991	static const unsigned long dot_string[] = {0x002e, 0x0000};  /* "." */
992	size_t length;
993	labellist_t l;
994
995	for (l = label, length = 0; l != NULL; l = l->next)
996		length += idn_ucs4_strlen(l->name) + 1;  /* name + `.' */
997	length++;  /* for NUL */
998
999	if (name_length < length)
1000		return (idn_buffer_overflow);
1001
1002	*name = '\0';
1003	for (l = label; l != NULL; l = l->next) {
1004		idn_ucs4_strcat(name, l->name);
1005		name += idn_ucs4_strlen(name);
1006		if (l->dot_followed)
1007			idn_ucs4_strcat(name, dot_string);
1008	}
1009	return (idn_success);
1010}
1011
1012static void
1013labellist_undo(labellist_t label) {
1014	size_t length;
1015
1016	length = idn_ucs4_strlen(label->undo_name) + 1;
1017	memcpy(label->name, label->undo_name, sizeof(long) * length);
1018}
1019
1020static labellist_t
1021labellist_tail(labellist_t label) {
1022	labellist_t l;
1023
1024	if (label == NULL)
1025		return (NULL);
1026	for (l = label; l->next != NULL; l = l->next)
1027		;  /* nothing to be done */
1028	return (l);
1029}
1030
1031static labellist_t
1032labellist_previous(labellist_t label) {
1033	return (label->previous);
1034}
1035
1036#ifndef WITHOUT_ICONV
1037
1038static idn_result_t
1039label_localdecodecheck(idn_resconf_t ctx, labellist_t label) {
1040	idn_converter_t local_converter = NULL;
1041	const unsigned long *from;
1042	char *to = NULL;
1043	size_t to_length;
1044	idn_result_t r;
1045
1046	from = labellist_getname(label);
1047	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1048	TRACE(("res ucs4tolocal_check(label=\"%s\")\n",
1049	       idn__debug_ucs4xstring(from, 50)));
1050
1051	local_converter = idn_resconf_getlocalconverter(ctx);
1052	if (local_converter == NULL) {
1053		r = idn_success;
1054		goto ret;
1055	}
1056
1057	for (;;) {
1058		char *new_buffer;
1059
1060		new_buffer = (char *)realloc(to, to_length);
1061		if (new_buffer == NULL) {
1062			r = idn_nomemory;
1063			goto ret;
1064		}
1065		to = new_buffer;
1066		r = idn_converter_convfromucs4(local_converter, from, to,
1067					       to_length);
1068		if (r == idn_success)
1069			break;
1070		else if (r == idn_nomapping) {
1071			r = label_idnencode_ace(ctx, label);
1072			if (r != idn_success)
1073				goto ret;
1074			break;
1075		} else if (r != idn_buffer_overflow) {
1076			goto ret;
1077		}
1078		to_length *= 2;
1079	}
1080
1081	r = idn_success;
1082ret:
1083	TRACE(("res ucs4tolocal_check(): %s\n", idn_result_tostring(r)));
1084	if (local_converter != NULL)
1085		idn_converter_destroy(local_converter);
1086	free(to);
1087	return (r);
1088}
1089
1090#endif /* !WITHOUT_ICONV */
1091
1092static idn_result_t
1093label_idndecode(idn_resconf_t ctx, labellist_t label) {
1094	idn_converter_t idn_converter = NULL;
1095	const unsigned long *from;
1096	char *ascii_from = NULL;
1097	unsigned long *to = NULL;
1098	size_t from_length, to_length;
1099	idn_result_t r;
1100
1101	from = labellist_getname(label);
1102	from_length = idn_ucs4_strlen(from) + 1;
1103	TRACE(("res idntoucs4(label=\"%s\")\n",
1104	       idn__debug_ucs4xstring(from, 50)));
1105
1106	idn_converter = idn_resconf_getidnconverter(ctx);
1107	if (idn_converter == NULL) {
1108		r = idn_success;
1109		goto ret;
1110	}
1111
1112	for (;;) {
1113		char *new_buffer;
1114
1115		new_buffer = (char *) realloc(ascii_from, from_length);
1116		if (new_buffer == NULL) {
1117			r = idn_nomemory;
1118			goto ret;
1119		}
1120		ascii_from = new_buffer;
1121		r = idn_ucs4_ucs4toutf8(from, ascii_from, from_length);
1122		if (r == idn_success)
1123			break;
1124		else if (r != idn_buffer_overflow)
1125			goto ret;
1126		from_length *= 2;
1127	}
1128
1129	to = NULL;
1130	to_length = from_length;
1131
1132	for (;;) {
1133		unsigned long *new_buffer;
1134
1135		new_buffer = (unsigned long *)
1136			     realloc(to, sizeof(long) * to_length);
1137		if (new_buffer == NULL) {
1138			r = idn_nomemory;
1139			goto ret;
1140		}
1141		to = new_buffer;
1142		r = idn_converter_convtoucs4(idn_converter, ascii_from, to,
1143					     to_length);
1144		if (r == idn_success)
1145			break;
1146		else if (r != idn_buffer_overflow)
1147			goto ret;
1148		to_length *= 2;
1149	}
1150
1151	r = labellist_setname(label, to);
1152ret:
1153	if (r == idn_success) {
1154		TRACE(("res idntoucs4(): success (label=\"%s\")\n",
1155		       idn__debug_ucs4xstring(labellist_getname(label),
1156					      50)));
1157	} else {
1158		TRACE(("res idntoucs4(): %s\n", idn_result_tostring(r)));
1159	}
1160	if (idn_converter != NULL)
1161		idn_converter_destroy(idn_converter);
1162	free(to);
1163	free(ascii_from);
1164	return (r);
1165}
1166
1167static idn_result_t
1168label_idnencode_ace(idn_resconf_t ctx, labellist_t label) {
1169	idn_converter_t idn_converter = NULL;
1170	const unsigned long *from;
1171	char *ascii_to = NULL;
1172	unsigned long *to = NULL;
1173	size_t to_length;
1174	idn_result_t r;
1175
1176	from = labellist_getname(label);
1177	TRACE(("res ucs4toidn(label=\"%s\")\n",
1178	       idn__debug_ucs4xstring(from, 50)));
1179
1180	idn_converter = idn_resconf_getidnconverter(ctx);
1181	if (idn_converter == NULL) {
1182		r = idn_success;
1183		goto ret;
1184	}
1185
1186	ascii_to = NULL;
1187	to_length = idn_ucs4_strlen(from) * 4 + 16;  /* add mergin */
1188
1189	for (;;) {
1190		char *new_buffer;
1191
1192		new_buffer = (char *) realloc(ascii_to, to_length);
1193		if (new_buffer == NULL) {
1194			r = idn_nomemory;
1195			goto ret;
1196		}
1197		ascii_to = new_buffer;
1198		r = idn_converter_convfromucs4(idn_converter, from, ascii_to,
1199					       to_length);
1200		if (r == idn_success)
1201			break;
1202		else if (r != idn_buffer_overflow)
1203			goto ret;
1204		to_length *= 2;
1205	}
1206
1207	for (;;) {
1208		unsigned long *new_buffer;
1209
1210		new_buffer = (unsigned long *)
1211			     realloc(to, sizeof(long) * to_length);
1212		if (new_buffer == NULL) {
1213			r = idn_nomemory;
1214			goto ret;
1215		}
1216		to = new_buffer;
1217		r = idn_ucs4_utf8toucs4(ascii_to, to, to_length);
1218		if (r == idn_success)
1219			break;
1220		else if (r != idn_buffer_overflow)
1221			goto ret;
1222		to_length *= 2;
1223	}
1224
1225	if (r != idn_success)
1226		goto ret;
1227
1228	r = labellist_setname(label, to);
1229ret:
1230	if (r == idn_success) {
1231		TRACE(("res ucs4toidn(): success (label=\"%s\")\n",
1232		       idn__debug_ucs4xstring(labellist_getname(label),
1233					      50)));
1234	} else {
1235		TRACE(("res ucs4toidn(): %s\n", idn_result_tostring(r)));
1236	}
1237	if (idn_converter != NULL)
1238		idn_converter_destroy(idn_converter);
1239	free(to);
1240	free(ascii_to);
1241	return (r);
1242}
1243
1244static idn_result_t
1245label_localmap(idn_resconf_t ctx, labellist_t label) {
1246	const unsigned long *from;
1247	const unsigned long *tld;
1248	unsigned long *to = NULL;
1249	size_t to_length;
1250	idn_mapselector_t local_mapper;
1251	idn_result_t r;
1252
1253	from = labellist_getname(label);
1254	tld = labellist_gettldname(label);
1255	TRACE(("res localmap(label=\"%s\", tld=\"%s\")\n",
1256	       idn__debug_ucs4xstring(from, 50),
1257	       idn__debug_ucs4xstring(tld, 50)));
1258
1259	local_mapper = idn_resconf_getlocalmapselector(ctx);
1260	if (local_mapper == NULL) {
1261		r = idn_success;
1262		goto ret;
1263	}
1264
1265	if (tld == from)
1266		tld = idn_mapselector_getdefaulttld();
1267	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1268
1269	for (;;) {
1270		unsigned long *new_buffer;
1271
1272		new_buffer = (unsigned long *)
1273			     realloc(to, sizeof(long) * to_length);
1274		if (new_buffer == NULL) {
1275			r = idn_nomemory;
1276			goto ret;
1277		}
1278		to = new_buffer;
1279		r = idn_mapselector_map2(local_mapper, from, tld, to,
1280					 to_length);
1281		if (r == idn_success)
1282			break;
1283		else if (r != idn_buffer_overflow)
1284			goto ret;
1285		to_length *= 2;
1286	}
1287
1288	r = labellist_setname(label, to);
1289ret:
1290	if (r == idn_success) {
1291		TRACE(("res localmap(): success (label=\"%s\")\n",
1292		       idn__debug_ucs4xstring(labellist_getname(label),
1293					      50)));
1294	} else {
1295		TRACE(("res localmap(): %s\n", idn_result_tostring(r)));
1296	}
1297	if (local_mapper != NULL)
1298		idn_mapselector_destroy(local_mapper);
1299	free(to);
1300	return (r);
1301}
1302
1303static idn_result_t
1304label_map(idn_resconf_t ctx, labellist_t label) {
1305	const unsigned long *from;
1306	unsigned long *to = NULL;
1307	size_t to_length;
1308	idn_mapper_t mapper;
1309	idn_result_t r;
1310
1311	from = labellist_getname(label);
1312	TRACE(("res map(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50)));
1313
1314	mapper = idn_resconf_getmapper(ctx);
1315	if (mapper == NULL) {
1316		r = idn_success;
1317		goto ret;
1318	}
1319	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1320
1321	for (;;) {
1322		unsigned long *new_buffer;
1323
1324		new_buffer = (unsigned long *)
1325			     realloc(to, sizeof(long) * to_length);
1326		if (new_buffer == NULL) {
1327			r = idn_nomemory;
1328			goto ret;
1329		}
1330		to = new_buffer;
1331		r = idn_mapper_map(mapper, from, to, to_length);
1332		if (r == idn_success)
1333			break;
1334		else if (r != idn_buffer_overflow)
1335			goto ret;
1336		to_length *= 2;
1337	}
1338
1339	r = labellist_setname(label, to);
1340ret:
1341	if (r == idn_success) {
1342		TRACE(("res map(): success (label=\"%s\")\n",
1343		       idn__debug_ucs4xstring(labellist_getname(label),
1344					      50)));
1345	} else {
1346		TRACE(("res map(): %s\n", idn_result_tostring(r)));
1347	}
1348	if (mapper != NULL)
1349		idn_mapper_destroy(mapper);
1350	free(to);
1351	return (r);
1352}
1353
1354static idn_result_t
1355label_normalize(idn_resconf_t ctx, labellist_t label) {
1356	const unsigned long *from;
1357	unsigned long *to = NULL;
1358	size_t to_length;
1359	idn_normalizer_t normalizer;
1360	idn_result_t r;
1361
1362	from = labellist_getname(label);
1363	TRACE(("res normalzie(label=\"%s\")\n",
1364	       idn__debug_ucs4xstring(from, 50)));
1365
1366	normalizer = idn_resconf_getnormalizer(ctx);
1367	if (normalizer == NULL) {
1368		r = idn_success;
1369		goto ret;
1370	}
1371	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1372
1373	for (;;) {
1374		unsigned long *new_buffer;
1375
1376		new_buffer = (unsigned long *)
1377			     realloc(to, sizeof(long) * to_length);
1378		if (new_buffer == NULL) {
1379			r = idn_nomemory;
1380			goto ret;
1381		}
1382		to = new_buffer;
1383		r = idn_normalizer_normalize(normalizer, from, to, to_length);
1384		if (r == idn_success)
1385			break;
1386		else if (r != idn_buffer_overflow)
1387			goto ret;
1388		to_length *= 2;
1389	}
1390
1391	r = labellist_setname(label, to);
1392ret:
1393	if (r == idn_success) {
1394		TRACE(("res normalize(): success (label=\"%s\")\n",
1395		       idn__debug_ucs4xstring(labellist_getname(label),
1396					      50)));
1397	} else {
1398		TRACE(("res normalize(): %s\n", idn_result_tostring(r)));
1399	}
1400	if (normalizer != NULL)
1401		idn_normalizer_destroy(normalizer);
1402	free(to);
1403	return (r);
1404}
1405
1406static idn_result_t
1407label_prohcheck(idn_resconf_t ctx, labellist_t label) {
1408	const unsigned long *name, *found;
1409	idn_checker_t prohibit_checker;
1410	idn_result_t r;
1411
1412	name = labellist_getname(label);
1413	TRACE(("res prohcheck(label=\"%s\")\n",
1414	       idn__debug_ucs4xstring(name, 50)));
1415
1416	prohibit_checker = idn_resconf_getprohibitchecker(ctx);
1417	if (prohibit_checker == NULL) {
1418		r = idn_success;
1419		goto ret;
1420	}
1421
1422	r = idn_checker_lookup(prohibit_checker, name, &found);
1423	idn_checker_destroy(prohibit_checker);
1424	if (r == idn_success && found != NULL)
1425		r = idn_prohibited;
1426
1427ret:
1428	TRACE(("res prohcheck(): %s\n", idn_result_tostring(r)));
1429	return (r);
1430}
1431
1432static idn_result_t
1433label_unascheck(idn_resconf_t ctx, labellist_t label) {
1434	const unsigned long *name, *found;
1435	idn_checker_t unassigned_checker;
1436	idn_result_t r;
1437
1438	name = labellist_getname(label);
1439	TRACE(("res unascheck(label=\"%s\")\n",
1440	       idn__debug_ucs4xstring(name, 50)));
1441
1442	unassigned_checker = idn_resconf_getunassignedchecker(ctx);
1443	if (unassigned_checker == NULL) {
1444		r = idn_success;
1445		goto ret;
1446	}
1447
1448	r = idn_checker_lookup(unassigned_checker, name, &found);
1449	idn_checker_destroy(unassigned_checker);
1450	if (r == idn_success && found != NULL)
1451		r = idn_prohibited;
1452
1453ret:
1454	TRACE(("res unascheck(): %s\n", idn_result_tostring(r)));
1455	return (r);
1456}
1457
1458static idn_result_t
1459label_bidicheck(idn_resconf_t ctx, labellist_t label) {
1460	const unsigned long *name, *found;
1461	idn_checker_t bidi_checker;
1462	idn_result_t r;
1463
1464	name = labellist_getname(label);
1465	TRACE(("res bidicheck(label=\"%s\")\n",
1466	       idn__debug_ucs4xstring(name, 50)));
1467
1468	bidi_checker = idn_resconf_getbidichecker(ctx);
1469	if (bidi_checker == NULL) {
1470		r = idn_success;
1471		goto ret;
1472	}
1473
1474	r = idn_checker_lookup(bidi_checker, name, &found);
1475	idn_checker_destroy(bidi_checker);
1476	if (r == idn_success && found != NULL)
1477		r = idn_prohibited;
1478
1479ret:
1480	TRACE(("res bidicheck(): %s\n", idn_result_tostring(r)));
1481	return (r);
1482}
1483
1484static idn_result_t
1485label_asccheck(idn_resconf_t ctx, labellist_t label) {
1486	const unsigned long *name, *n;
1487	idn_result_t r;
1488
1489	name = labellist_getname(label);
1490	TRACE(("res asccheck(label=\"%s\")\n",
1491	       idn__debug_ucs4xstring(name, 50)));
1492
1493	if (*name == '-') {
1494		r = idn_prohibited;
1495		goto ret;
1496	}
1497
1498	for (n = name; *n != '\0'; n++) {
1499		if (*n <= '\177') {
1500			if ((*n < '0' || *n > '9') &&
1501			    (*n < 'A' || *n > 'Z') &&
1502			    (*n < 'a' || *n > 'z') &&
1503			    *n != '-') {
1504				r  = idn_prohibited;
1505				goto ret;
1506			}
1507		}
1508	}
1509
1510	if (n > name && *(n - 1) == '-') {
1511		r  = idn_prohibited;
1512		goto ret;
1513	}
1514
1515	r = idn_success;
1516ret:
1517	TRACE(("res asccheck(): %s\n", idn_result_tostring(r)));
1518	return (r);
1519}
1520
1521static idn_result_t
1522label_lencheck_ace(idn_resconf_t ctx, labellist_t label) {
1523	const unsigned long *name;
1524	size_t name_length;
1525	idn_result_t r;
1526
1527	name = labellist_getname(label);
1528	name_length = idn_ucs4_strlen(name);
1529	TRACE(("res lencheck(label=\"%s\")\n",
1530	       idn__debug_ucs4xstring(name, 50)));
1531
1532	if (name_length == 0 || name_length > MAX_LABEL_LENGTH) {
1533		r = idn_invalid_length;
1534		goto ret;
1535	}
1536
1537	r = idn_success;
1538ret:
1539	TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
1540	return (r);
1541}
1542
1543static idn_result_t
1544label_lencheck_nonace(idn_resconf_t ctx, labellist_t label) {
1545	idn_converter_t idn_converter;
1546	const unsigned long *from;
1547	size_t to_length;
1548	idn_result_t r;
1549	char *buffer = NULL;
1550	size_t buffer_length;
1551
1552	from = labellist_getname(label);
1553	TRACE(("res lencheck(label=\"%s\")\n",
1554	       idn__debug_ucs4xstring(from, 50)));
1555
1556	buffer_length = idn_ucs4_strlen(from) * 4 + 16; /* 16 for margin */
1557	idn_converter = idn_resconf_getidnconverter(ctx);
1558
1559	for (;;) {
1560		void *new_buffer;
1561
1562		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
1563		if (new_buffer == NULL) {
1564			r = idn_nomemory;
1565			goto ret;
1566		}
1567		buffer = (char *)new_buffer;
1568
1569		if (idn_converter != NULL) {
1570			r = idn_converter_convfromucs4(idn_converter, from,
1571						       buffer, buffer_length);
1572		} else {
1573			r = idn_ucs4_ucs4toutf8(from, buffer, buffer_length);
1574		}
1575		if (r == idn_success)
1576			break;
1577		else if (r != idn_buffer_overflow)
1578			goto ret;
1579
1580		buffer_length *= 2;
1581	}
1582
1583	to_length = strlen(buffer);
1584	if (to_length == 0 || to_length > MAX_LABEL_LENGTH) {
1585		r = idn_invalid_length;
1586		goto ret;
1587	}
1588
1589	r = idn_success;
1590ret:
1591	TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
1592	if (idn_converter != NULL)
1593		idn_converter_destroy(idn_converter);
1594	free(buffer);
1595	return (r);
1596}
1597
1598static idn_result_t
1599label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label,
1600	    const unsigned long *original_name) {
1601	labellist_t rt_label = NULL;
1602	const unsigned long *rt_name;
1603	const unsigned long *cur_name;
1604	idn_result_t r;
1605
1606	cur_name = labellist_getname(label);
1607	TRACE(("res rtcheck(label=\"%s\", org_label=\"%s\")\n",
1608		idn__debug_ucs4xstring(cur_name, 50),
1609		idn__debug_ucs4xstring(original_name, 50)));
1610
1611	r = labellist_create(cur_name, &rt_label);
1612	if (r != idn_success)
1613		goto ret;
1614	if (rt_label == NULL) {
1615		if (*original_name == '\0')
1616			r = idn_success;
1617		else
1618			r = idn_invalid_encoding;
1619		goto ret;
1620	}
1621
1622	if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
1623		r = label_map(ctx, rt_label);
1624		if (r != idn_success)
1625			goto ret;
1626		r = label_normalize(ctx, rt_label);
1627		if (r != idn_success)
1628			goto ret;
1629		r = label_prohcheck(ctx, rt_label);
1630		if (r != idn_success)
1631			goto ret;
1632		if (actions & IDN_UNASCHECK) {
1633			r = label_unascheck(ctx, rt_label);
1634			if (r != idn_success)
1635				goto ret;
1636		}
1637		r = label_bidicheck(ctx, rt_label);
1638		if (r != idn_success)
1639			goto ret;
1640	}
1641
1642	if (actions & IDN_ASCCHECK) {
1643		r = label_asccheck(ctx, rt_label);
1644		if (r != idn_success)
1645			goto ret;
1646	}
1647	if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
1648		r = label_idnencode_ace(ctx, rt_label);
1649		if (r != idn_success)
1650			goto ret;
1651	}
1652	r = label_lencheck_ace(ctx, rt_label);
1653	if (r != idn_success)
1654		goto ret;
1655	rt_name = labellist_getname(rt_label);
1656
1657	if (idn_ucs4_strcasecmp(rt_name, original_name) != 0) {
1658		TRACE(("res rtcheck(): round trip failed, org =\"%s\", rt=\"%s\"\n",
1659		       idn__debug_ucs4xstring(original_name, 50),
1660		       idn__debug_ucs4xstring(rt_name, 50)));
1661		r = idn_invalid_encoding;
1662		goto ret;
1663	}
1664
1665	r  = idn_success;
1666ret:
1667	if (r != idn_nomemory && r != idn_success)
1668		r = idn_invalid_encoding;
1669	TRACE(("res rtcheck(): %s\n", idn_result_tostring(r)));
1670	if (rt_label != NULL)
1671		labellist_destroy(rt_label);
1672	return (r);
1673}
1674
1675const char *
1676idn__res_actionstostring(idn_action_t actions) {
1677	static char buf[100];
1678
1679	buf[0] = '\0';
1680
1681	if (actions == IDN_ENCODE_QUERY)
1682		strcpy(buf, "encode-query");
1683	else if (actions == IDN_DECODE_QUERY)
1684		strcpy(buf, "decode-query");
1685	else if (actions == IDN_ENCODE_APP)
1686		strcpy(buf, "encode-app");
1687	else if (actions == IDN_DECODE_APP)
1688		strcpy(buf, "decode-app");
1689	else if (actions == IDN_ENCODE_STORED)
1690		strcpy(buf, "encode-stored");
1691	else if (actions == IDN_DECODE_STORED)
1692		strcpy(buf, "decode-stored");
1693	else {
1694		if (actions & IDN_LOCALCONV)
1695			strcat(buf, "|localconv");
1696		if (actions & IDN_DELIMMAP)
1697			strcat(buf, "|delimmap");
1698		if (actions & IDN_LOCALMAP)
1699			strcat(buf, "|localmap");
1700
1701		if (actions & IDN_MAP)
1702			strcat(buf, "|map");
1703		if (actions & IDN_NORMALIZE)
1704			strcat(buf, "|normalize");
1705		if (actions & IDN_PROHCHECK)
1706			strcat(buf, "|prohcheck");
1707		if (actions & IDN_UNASCHECK)
1708			strcat(buf, "|unascheck");
1709		if (actions & IDN_BIDICHECK)
1710			strcat(buf, "|bidicheck");
1711
1712		if (actions & IDN_IDNCONV)
1713			strcat(buf, "|idnconv");
1714		if (actions & IDN_ASCCHECK)
1715			strcat(buf, "|asccheck");
1716		if (actions & IDN_LENCHECK)
1717			strcat(buf, "|lencheck");
1718		if (actions & IDN_RTCHECK)
1719			strcat(buf, "|rtcheck");
1720	}
1721
1722	if (buf[0] == '|')
1723		return (buf + 1);
1724	else
1725		return (buf);
1726}
1727