1/*	$NetBSD: mapper.c,v 1.2 2011/02/16 03:46:56 christos Exp $	*/
2
3#ifndef lint
4static char *rcsid = "Id: mapper.c,v 1.1 2003/06/04 00:25:55 marka Exp ";
5#endif
6
7/*
8 * Copyright (c) 2001,2002 Japan Network Information Center.
9 * All rights reserved.
10 *
11 * By using this file, you agree to the terms and conditions set forth bellow.
12 *
13 * 			LICENSE TERMS AND CONDITIONS
14 *
15 * The following License Terms and Conditions apply, unless a different
16 * license is obtained from Japan Network Information Center ("JPNIC"),
17 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
18 * Chiyoda-ku, Tokyo 101-0047, Japan.
19 *
20 * 1. Use, Modification and Redistribution (including distribution of any
21 *    modified or derived work) in source and/or binary forms is permitted
22 *    under this License Terms and Conditions.
23 *
24 * 2. Redistribution of source code must retain the copyright notices as they
25 *    appear in each source code file, this License Terms and Conditions.
26 *
27 * 3. Redistribution in binary form must reproduce the Copyright Notice,
28 *    this License Terms and Conditions, in the documentation and/or other
29 *    materials provided with the distribution.  For the purposes of binary
30 *    distribution the "Copyright Notice" refers to the following language:
31 *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
32 *
33 * 4. The name of JPNIC may not be used to endorse or promote products
34 *    derived from this Software without specific prior written approval of
35 *    JPNIC.
36 *
37 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
38 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
40 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
41 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
44 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
45 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
46 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
47 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
48 */
49
50#include <config.h>
51
52#include <stddef.h>
53#include <stdlib.h>
54#include <string.h>
55
56#include <idn/result.h>
57#include <idn/assert.h>
58#include <idn/logmacro.h>
59#include <idn/mapper.h>
60#include <idn/strhash.h>
61#include <idn/debug.h>
62#include <idn/util.h>
63#include <idn/ucs4.h>
64
65/*
66 * Type for mapping scheme.
67 */
68typedef struct {
69	char *prefix;
70	char *parameter;
71	idn_mapper_createproc_t create;
72	idn_mapper_destroyproc_t destroy;
73	idn_mapper_mapproc_t map;
74	void *context;
75} map_scheme_t;
76
77/*
78 * Standard mapping schemes.
79 */
80static const map_scheme_t nameprep_scheme = {
81	"RFC3491",
82	NULL,
83	idn_nameprep_createproc,
84	idn_nameprep_destroyproc,
85	idn_nameprep_mapproc,
86	NULL,
87};
88
89static const map_scheme_t filemap_scheme = {
90	"filemap",
91	"",
92	idn__filemapper_createproc,
93	idn__filemapper_destroyproc,
94	idn__filemapper_mapproc,
95	NULL,
96};
97
98static const map_scheme_t *standard_map_schemes[] = {
99	&nameprep_scheme,
100	&filemap_scheme,
101	NULL,
102};
103
104/*
105 * Hash table for mapping schemes.
106 */
107static idn__strhash_t scheme_hash = NULL;
108
109/*
110 * Mapper object type.
111 */
112struct idn_mapper {
113	int nschemes;
114	int scheme_size;
115	map_scheme_t *schemes;
116	int reference_count;
117};
118
119#define MAPPER_INITIAL_SCHEME_SIZE	1
120
121idn_result_t
122idn_mapper_initialize(void) {
123	idn_result_t r;
124	map_scheme_t **scheme;
125
126	TRACE(("idn_mapper_initialize()\n"));
127
128	if (scheme_hash != NULL) {
129		r = idn_success;	/* already initialized */
130		goto ret;
131	}
132
133	r = idn__strhash_create(&scheme_hash);
134	if (r != idn_success)
135		goto ret;
136
137	for (scheme = (map_scheme_t **)standard_map_schemes;
138		*scheme != NULL; scheme++) {
139		r = idn__strhash_put(scheme_hash, (*scheme)->prefix, *scheme);
140		if (r != idn_success)
141			goto ret;
142	}
143
144	r = idn_success;
145ret:
146	if (r != idn_success && scheme_hash != NULL) {
147		idn__strhash_destroy(scheme_hash, NULL);
148		scheme_hash = NULL;
149	}
150	TRACE(("idn_mapper_initialize(): %s\n", idn_result_tostring(r)));
151	return (r);
152}
153
154idn_result_t
155idn_mapper_create(idn_mapper_t *ctxp) {
156	idn_mapper_t ctx = NULL;
157	idn_result_t r;
158
159	assert(scheme_hash != NULL);
160	assert(ctxp != NULL);
161
162	TRACE(("idn_mapper_create()\n"));
163
164	ctx = (idn_mapper_t) malloc(sizeof(struct idn_mapper));
165	if (ctx == NULL) {
166		r = idn_nomemory;
167		goto ret;
168	}
169
170	ctx->schemes = (map_scheme_t *) malloc(sizeof(map_scheme_t)
171		 * MAPPER_INITIAL_SCHEME_SIZE);
172	if (ctx->schemes == NULL) {
173		r = idn_nomemory;
174		goto ret;
175	}
176
177	ctx->nschemes = 0;
178	ctx->scheme_size = MAPPER_INITIAL_SCHEME_SIZE;
179	ctx->reference_count = 1;
180	*ctxp = ctx;
181	r = idn_success;
182
183ret:
184	if (r != idn_success) {
185		if (ctx != NULL)
186			free(ctx->schemes);
187		free(ctx);
188	}
189	TRACE(("idn_mapper_create(): %s\n", idn_result_tostring(r)));
190	return (r);
191}
192
193void
194idn_mapper_destroy(idn_mapper_t ctx) {
195	int i;
196
197	assert(scheme_hash != NULL);
198	assert(ctx != NULL);
199
200	TRACE(("idn_mapper_destroy()\n"));
201
202	ctx->reference_count--;
203	if (ctx->reference_count <= 0) {
204		TRACE(("idn_mapper_destroy(): the object is destroyed\n"));
205		for (i = 0; i < ctx->nschemes; i++)
206			ctx->schemes[i].destroy(ctx->schemes[i].context);
207		free(ctx->schemes);
208		free(ctx);
209	} else {
210		TRACE(("idn_mapper_destroy(): "
211		       "update reference count (%d->%d)\n",
212		       ctx->reference_count + 1, ctx->reference_count));
213	}
214}
215
216void
217idn_mapper_incrref(idn_mapper_t ctx) {
218	assert(ctx != NULL && scheme_hash != NULL);
219
220	TRACE(("idn_mapper_incrref()\n"));
221	TRACE(("idn_mapper_incrref: update reference count (%d->%d)\n",
222		ctx->reference_count, ctx->reference_count + 1));
223
224	ctx->reference_count++;
225}
226
227idn_result_t
228idn_mapper_add(idn_mapper_t ctx, const char *scheme_name) {
229	idn_result_t r;
230	map_scheme_t *scheme;
231	const char *scheme_prefix;
232	const char *scheme_parameter;
233	void *scheme_context = NULL;
234	char static_buffer[128];	/* large enough */
235	char *buffer = static_buffer;
236
237	assert(scheme_hash != NULL);
238	assert(ctx != NULL);
239
240	TRACE(("idn_mapper_add(scheme_name=%s)\n",
241		idn__debug_xstring(scheme_name, 50)));
242
243	/*
244	 * Split `scheme_name' into `scheme_prefix' and `scheme_parameter'.
245	 */
246	scheme_parameter = strchr(scheme_name, ':');
247	if (scheme_parameter == NULL) {
248		scheme_prefix = scheme_name;
249	} else {
250		ptrdiff_t scheme_prefixlen;
251
252		scheme_prefixlen = scheme_parameter - scheme_name;
253		if (scheme_prefixlen + 1 > sizeof(static_buffer)) {
254			buffer = (char *) malloc(scheme_prefixlen + 1);
255			if (buffer == NULL) {
256				r = idn_nomemory;
257				goto ret;
258			}
259		}
260		memcpy(buffer, scheme_name, scheme_prefixlen);
261		*(buffer + scheme_prefixlen) = '\0';
262		scheme_prefix = buffer;
263		scheme_parameter++;
264	}
265
266	/*
267	 * Find a scheme.
268	 */
269	if (idn__strhash_get(scheme_hash, scheme_prefix, (void **)&scheme)
270		!= idn_success) {
271		ERROR(("idn_mapper_add(): invalid scheme name \"%-.30s\"\n",
272		       scheme_prefix));
273		r = idn_invalid_name;
274		goto ret;
275	}
276	if (scheme_parameter == NULL) {
277		if (scheme->parameter != NULL)
278			scheme_parameter = scheme->parameter;
279		else
280			scheme_parameter = scheme->prefix;
281	}
282
283	/*
284	 * Add the scheme.
285	 */
286	assert(ctx->nschemes <= ctx->scheme_size);
287
288	if (ctx->nschemes == ctx->scheme_size) {
289		map_scheme_t *new_schemes;
290
291		new_schemes = (map_scheme_t *) realloc(ctx->schemes,
292			sizeof(map_scheme_t) * ctx->scheme_size * 2);
293		if (new_schemes == NULL) {
294			r = idn_nomemory;
295			goto ret;
296		}
297		ctx->schemes = new_schemes;
298		ctx->scheme_size *= 2;
299	}
300
301	r = scheme->create(scheme_parameter, &scheme_context);
302	if (r != idn_success)
303		goto ret;
304
305	memcpy(ctx->schemes + ctx->nschemes, scheme, sizeof(map_scheme_t));
306	ctx->schemes[ctx->nschemes].context = scheme_context;
307	ctx->nschemes++;
308	r = idn_success;
309ret:
310	if (r != idn_success)
311		free(scheme_context);
312	if (buffer != static_buffer)
313		free(buffer);
314	TRACE(("idn_mapper_add(): %s\n", idn_result_tostring(r)));
315	return (r);
316}
317
318idn_result_t
319idn_mapper_addall(idn_mapper_t ctx, const char **scheme_names, int nschemes) {
320	idn_result_t r;
321	int i;
322
323	assert(scheme_hash != NULL);
324	assert(ctx != NULL && scheme_names != NULL);
325
326	TRACE(("idn_mapper_addall(nschemes=%d)\n", nschemes));
327
328	for (i = 0; i < nschemes; i++) {
329		r = idn_mapper_add(ctx, (const char *)*scheme_names);
330		if (r != idn_success)
331			goto ret;
332		scheme_names++;
333	}
334
335	r = idn_success;
336ret:
337	TRACE(("idn_mapper_addall(): %s\n", idn_result_tostring(r)));
338	return (r);
339}
340
341idn_result_t
342idn_mapper_map(idn_mapper_t ctx, const unsigned long *from,
343	       unsigned long *to, size_t tolen) {
344	idn_result_t r;
345	unsigned long *src, *dst;
346	unsigned long *buffers[2] = {NULL, NULL};
347	size_t buflen[2] = {0, 0};
348	size_t dstlen;
349	int idx;
350	int i;
351
352	assert(scheme_hash != NULL);
353	assert(ctx != NULL && from != NULL && to != NULL);
354
355	TRACE(("idn_mapper_map(from=\"%s\", tolen=%d)\n",
356	       idn__debug_ucs4xstring(from, 50), (int)tolen));
357
358	if (ctx->nschemes <= 0) {
359		if (tolen < idn_ucs4_strlen(from) + 1) {
360			r = idn_buffer_overflow;
361			goto ret;
362		}
363		idn_ucs4_strcpy(to, from);
364		r = idn_success;
365		goto ret;
366	}
367
368	/*
369	 * Map.
370	 */
371	src = (void *)from;
372	dstlen = idn_ucs4_strlen(from) + 1;
373
374	i = 0;
375	while (i < ctx->nschemes) {
376		TRACE(("idn_mapper_map(): map %s\n", ctx->schemes[i].prefix));
377
378		/*
379		 * Choose destination area to restore the result of a mapping.
380		 */
381		if (i + 1 == ctx->nschemes) {
382			dst = to;
383			dstlen = tolen;
384
385		} else {
386			if (src == buffers[0])
387				idx = 1;
388			else
389				idx = 0;
390
391			if (buflen[idx] < dstlen) {
392				void *newbuf;
393
394				newbuf = realloc(buffers[idx],
395						 sizeof(long) * dstlen);
396				if (newbuf == NULL) {
397					r = idn_nomemory;
398					goto ret;
399				}
400				buffers[idx] = (unsigned long *)newbuf;
401				buflen[idx] = dstlen;
402			}
403
404			dst = buffers[idx];
405			dstlen = buflen[idx];
406		}
407
408		/*
409		 * Perform i-th map scheme.
410		 * If buffer size is not enough, we double it and try again.
411		 */
412		r = (ctx->schemes[i].map)(ctx->schemes[i].context, src, dst,
413					  dstlen);
414		if (r == idn_buffer_overflow && dst != to) {
415			dstlen *= 2;
416			continue;
417		}
418		if (r != idn_success)
419			goto ret;
420
421		src = dst;
422		i++;
423	}
424
425	r = idn_success;
426ret:
427	free(buffers[0]);
428	free(buffers[1]);
429	if (r == idn_success) {
430		TRACE(("idn_mapper_map(): success (to=\"%s\")\n",
431		       idn__debug_ucs4xstring(to, 50)));
432	} else {
433		TRACE(("idn_mapper_map(): %s\n", idn_result_tostring(r)));
434	}
435	return (r);
436}
437
438idn_result_t
439idn_mapper_register(const char *prefix,
440		    idn_mapper_createproc_t create,
441		    idn_mapper_destroyproc_t destroy,
442		    idn_mapper_mapproc_t map) {
443	idn_result_t r;
444	map_scheme_t *scheme = NULL;
445
446	assert(scheme_hash != NULL);
447	assert(prefix != NULL && create != NULL && destroy != NULL &&
448		map != NULL);
449
450	TRACE(("idn_mapper_register(prefix=%s)\n", prefix));
451
452	scheme = (map_scheme_t *) malloc(sizeof(map_scheme_t));
453	if (scheme == NULL) {
454		r = idn_nomemory;
455		goto ret;
456	}
457
458	scheme->prefix = (char *) malloc(strlen(prefix) + 1);
459	if (scheme->prefix == NULL) {
460		r = idn_nomemory;
461		goto ret;
462	}
463
464	strcpy(scheme->prefix, prefix);
465	scheme->parameter = NULL;
466	scheme->create    = create;
467	scheme->destroy   = destroy;
468	scheme->map       = map;
469
470	r = idn__strhash_put(scheme_hash, prefix, scheme);
471	if (r != idn_success)
472		goto ret;
473
474	r = idn_success;
475ret:
476	if (r != idn_success) {
477		if (scheme != NULL)
478			free(scheme->prefix);
479		free(scheme);
480	}
481
482	TRACE(("idn_mapper_register(): %s\n", idn_result_tostring(r)));
483	return (r);
484}
485