1 #ifndef lint
2 static char *rcsid = "$Id$";
3 #endif
4 
5 /*
6  * Copyright (c) 2001,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/mapper.h>
58 #include <idn/strhash.h>
59 #include <idn/debug.h>
60 #include <idn/util.h>
61 #include <idn/ucs4.h>
62 
63 /*
64  * Type for mapping scheme.
65  */
66 typedef struct {
67 	char *prefix;
68 	char *parameter;
69 	idn_mapper_createproc_t create;
70 	idn_mapper_destroyproc_t destroy;
71 	idn_mapper_mapproc_t map;
72 	void *context;
73 } map_scheme_t;
74 
75 /*
76  * Standard mapping schemes.
77  */
78 static const map_scheme_t nameprep_scheme = {
79 	"RFC3491",
80 	NULL,
81 	idn_nameprep_createproc,
82 	idn_nameprep_destroyproc,
83 	idn_nameprep_mapproc,
84 	NULL,
85 };
86 
87 static const map_scheme_t filemap_scheme = {
88 	"filemap",
89 	"",
90 	idn__filemapper_createproc,
91 	idn__filemapper_destroyproc,
92 	idn__filemapper_mapproc,
93 	NULL,
94 };
95 
96 static const map_scheme_t *standard_map_schemes[] = {
97 	&nameprep_scheme,
98 	&filemap_scheme,
99 	NULL,
100 };
101 
102 /*
103  * Hash table for mapping schemes.
104  */
105 static idn__strhash_t scheme_hash = NULL;
106 
107 /*
108  * Mapper object type.
109  */
110 struct idn_mapper {
111 	int nschemes;
112 	int scheme_size;
113 	map_scheme_t *schemes;
114 	int reference_count;
115 };
116 
117 #define MAPPER_INITIAL_SCHEME_SIZE	1
118 
119 idn_result_t
idn_mapper_initialize(void)120 idn_mapper_initialize(void) {
121 	idn_result_t r;
122 	map_scheme_t **scheme;
123 
124 	TRACE(("idn_mapper_initialize()\n"));
125 
126 	if (scheme_hash != NULL) {
127 		r = idn_success;	/* already initialized */
128 		goto ret;
129 	}
130 
131 	r = idn__strhash_create(&scheme_hash);
132 	if (r != idn_success)
133 		goto ret;
134 
135 	for (scheme = (map_scheme_t **)standard_map_schemes;
136 		*scheme != NULL; scheme++) {
137 		r = idn__strhash_put(scheme_hash, (*scheme)->prefix, *scheme);
138 		if (r != idn_success)
139 			goto ret;
140 	}
141 
142 	r = idn_success;
143 ret:
144 	if (r != idn_success && scheme_hash != NULL) {
145 		idn__strhash_destroy(scheme_hash, NULL);
146 		scheme_hash = NULL;
147 	}
148 	TRACE(("idn_mapper_initialize(): %s\n", idn_result_tostring(r)));
149 	return (r);
150 }
151 
152 idn_result_t
idn_mapper_create(idn_mapper_t * ctxp)153 idn_mapper_create(idn_mapper_t *ctxp) {
154 	idn_mapper_t ctx = NULL;
155 	idn_result_t r;
156 
157 	assert(scheme_hash != NULL);
158 	assert(ctxp != NULL);
159 
160 	TRACE(("idn_mapper_create()\n"));
161 
162 	ctx = (idn_mapper_t) malloc(sizeof(struct idn_mapper));
163 	if (ctx == NULL) {
164 		r = idn_nomemory;
165 		goto ret;
166 	}
167 
168 	ctx->schemes = (map_scheme_t *) malloc(sizeof(map_scheme_t)
169 		 * MAPPER_INITIAL_SCHEME_SIZE);
170 	if (ctx->schemes == NULL) {
171 		r = idn_nomemory;
172 		goto ret;
173 	}
174 
175 	ctx->nschemes = 0;
176 	ctx->scheme_size = MAPPER_INITIAL_SCHEME_SIZE;
177 	ctx->reference_count = 1;
178 	*ctxp = ctx;
179 	r = idn_success;
180 
181 ret:
182 	if (r != idn_success) {
183 		if (ctx != NULL)
184 			free(ctx->schemes);
185 		free(ctx);
186 	}
187 	TRACE(("idn_mapper_create(): %s\n", idn_result_tostring(r)));
188 	return (r);
189 }
190 
191 void
idn_mapper_destroy(idn_mapper_t ctx)192 idn_mapper_destroy(idn_mapper_t ctx) {
193 	int i;
194 
195 	assert(scheme_hash != NULL);
196 	assert(ctx != NULL);
197 
198 	TRACE(("idn_mapper_destroy()\n"));
199 
200 	ctx->reference_count--;
201 	if (ctx->reference_count <= 0) {
202 		TRACE(("idn_mapper_destroy(): the object is destroyed\n"));
203 		for (i = 0; i < ctx->nschemes; i++)
204 			ctx->schemes[i].destroy(ctx->schemes[i].context);
205 		free(ctx->schemes);
206 		free(ctx);
207 	} else {
208 		TRACE(("idn_mapper_destroy(): "
209 		       "update reference count (%d->%d)\n",
210 		       ctx->reference_count + 1, ctx->reference_count));
211 	}
212 }
213 
214 void
idn_mapper_incrref(idn_mapper_t ctx)215 idn_mapper_incrref(idn_mapper_t ctx) {
216 	assert(ctx != NULL && scheme_hash != NULL);
217 
218 	TRACE(("idn_mapper_incrref()\n"));
219 	TRACE(("idn_mapper_incrref: update reference count (%d->%d)\n",
220 		ctx->reference_count, ctx->reference_count + 1));
221 
222 	ctx->reference_count++;
223 }
224 
225 idn_result_t
idn_mapper_add(idn_mapper_t ctx,const char * scheme_name)226 idn_mapper_add(idn_mapper_t ctx, const char *scheme_name) {
227 	idn_result_t r;
228 	map_scheme_t *scheme;
229 	const char *scheme_prefix;
230 	const char *scheme_parameter;
231 	void *scheme_context = NULL;
232 	char static_buffer[128];	/* large enough */
233 	char *buffer = static_buffer;
234 
235 	assert(scheme_hash != NULL);
236 	assert(ctx != NULL);
237 
238 	TRACE(("idn_mapper_add(scheme_name=%s)\n",
239 		idn__debug_xstring(scheme_name, 50)));
240 
241 	/*
242 	 * Split `scheme_name' into `scheme_prefix' and `scheme_parameter'.
243 	 */
244 	scheme_parameter = strchr(scheme_name, ':');
245 	if (scheme_parameter == NULL) {
246 		scheme_prefix = scheme_name;
247 	} else {
248 		ptrdiff_t scheme_prefixlen;
249 
250 		scheme_prefixlen = scheme_parameter - scheme_name;
251 		if (scheme_prefixlen + 1 > sizeof(static_buffer)) {
252 			buffer = (char *) malloc(scheme_prefixlen + 1);
253 			if (buffer == NULL) {
254 				r = idn_nomemory;
255 				goto ret;
256 			}
257 		}
258 		memcpy(buffer, scheme_name, scheme_prefixlen);
259 		*(buffer + scheme_prefixlen) = '\0';
260 		scheme_prefix = buffer;
261 		scheme_parameter++;
262 	}
263 
264 	/*
265 	 * Find a scheme.
266 	 */
267 	if (idn__strhash_get(scheme_hash, scheme_prefix, (void **)&scheme)
268 		!= idn_success) {
269 		ERROR(("idn_mapper_add(): invalid scheme name \"%-.30s\"\n",
270 		       scheme_prefix));
271 		r = idn_invalid_name;
272 		goto ret;
273 	}
274 	if (scheme_parameter == NULL) {
275 		if (scheme->parameter != NULL)
276 			scheme_parameter = scheme->parameter;
277 		else
278 			scheme_parameter = scheme->prefix;
279 	}
280 
281 	/*
282 	 * Add the scheme.
283 	 */
284 	assert(ctx->nschemes <= ctx->scheme_size);
285 
286 	if (ctx->nschemes == ctx->scheme_size) {
287 		map_scheme_t *new_schemes;
288 
289 		new_schemes = (map_scheme_t *) realloc(ctx->schemes,
290 			sizeof(map_scheme_t) * ctx->scheme_size * 2);
291 		if (new_schemes == NULL) {
292 			r = idn_nomemory;
293 			goto ret;
294 		}
295 		ctx->schemes = new_schemes;
296 		ctx->scheme_size *= 2;
297 	}
298 
299 	r = scheme->create(scheme_parameter, &scheme_context);
300 	if (r != idn_success)
301 		goto ret;
302 
303 	memcpy(ctx->schemes + ctx->nschemes, scheme, sizeof(map_scheme_t));
304 	ctx->schemes[ctx->nschemes].context = scheme_context;
305 	ctx->nschemes++;
306 	r = idn_success;
307 ret:
308 	if (r != idn_success)
309 		free(scheme_context);
310 	if (buffer != static_buffer)
311 		free(buffer);
312 	TRACE(("idn_mapper_add(): %s\n", idn_result_tostring(r)));
313 	return (r);
314 }
315 
316 idn_result_t
idn_mapper_addall(idn_mapper_t ctx,const char ** scheme_names,int nschemes)317 idn_mapper_addall(idn_mapper_t ctx, const char **scheme_names, int nschemes) {
318 	idn_result_t r;
319 	int i;
320 
321 	assert(scheme_hash != NULL);
322 	assert(ctx != NULL && scheme_names != NULL);
323 
324 	TRACE(("idn_mapper_addall(nschemes=%d)\n", nschemes));
325 
326 	for (i = 0; i < nschemes; i++) {
327 		r = idn_mapper_add(ctx, (const char *)*scheme_names);
328 		if (r != idn_success)
329 			goto ret;
330 		scheme_names++;
331 	}
332 
333 	r = idn_success;
334 ret:
335 	TRACE(("idn_mapper_addall(): %s\n", idn_result_tostring(r)));
336 	return (r);
337 }
338 
339 idn_result_t
idn_mapper_map(idn_mapper_t ctx,const unsigned long * from,unsigned long * to,size_t tolen)340 idn_mapper_map(idn_mapper_t ctx, const unsigned long *from,
341 	       unsigned long *to, size_t tolen) {
342 	idn_result_t r;
343 	unsigned long *src, *dst;
344 	unsigned long *buffers[2] = {NULL, NULL};
345 	size_t buflen[2] = {0, 0};
346 	size_t dstlen;
347 	int idx;
348 	int i;
349 
350 	assert(scheme_hash != NULL);
351 	assert(ctx != NULL && from != NULL && to != NULL);
352 
353 	TRACE(("idn_mapper_map(from=\"%s\", tolen=%d)\n",
354 	       idn__debug_ucs4xstring(from, 50), (int)tolen));
355 
356 	if (ctx->nschemes <= 0) {
357 		if (tolen < idn_ucs4_strlen(from) + 1) {
358 			r = idn_buffer_overflow;
359 			goto ret;
360 		}
361 		idn_ucs4_strcpy(to, from);
362 		r = idn_success;
363 		goto ret;
364 	}
365 
366 	/*
367 	 * Map.
368 	 */
369 	src = (void *)from;
370 	dstlen = idn_ucs4_strlen(from) + 1;
371 
372 	i = 0;
373 	while (i < ctx->nschemes) {
374 		TRACE(("idn_mapper_map(): map %s\n", ctx->schemes[i].prefix));
375 
376 		/*
377 		 * Choose destination area to restore the result of a mapping.
378 		 */
379 		if (i + 1 == ctx->nschemes) {
380 			dst = to;
381 			dstlen = tolen;
382 
383 		} else {
384 			if (src == buffers[0])
385 				idx = 1;
386 			else
387 				idx = 0;
388 
389 			if (buflen[idx] < dstlen) {
390 				void *newbuf;
391 
392 				newbuf = realloc(buffers[idx],
393 						 sizeof(long) * dstlen);
394 				if (newbuf == NULL) {
395 					r = idn_nomemory;
396 					goto ret;
397 				}
398 				buffers[idx] = (unsigned long *)newbuf;
399 				buflen[idx] = dstlen;
400 			}
401 
402 			dst = buffers[idx];
403 			dstlen = buflen[idx];
404 		}
405 
406 		/*
407 		 * Perform i-th map scheme.
408 		 * If buffer size is not enough, we double it and try again.
409 		 */
410 		r = (ctx->schemes[i].map)(ctx->schemes[i].context, src, dst,
411 					  dstlen);
412 		if (r == idn_buffer_overflow && dst != to) {
413 			dstlen *= 2;
414 			continue;
415 		}
416 		if (r != idn_success)
417 			goto ret;
418 
419 		src = dst;
420 		i++;
421 	}
422 
423 	r = idn_success;
424 ret:
425 	free(buffers[0]);
426 	free(buffers[1]);
427 	if (r == idn_success) {
428 		TRACE(("idn_mapper_map(): success (to=\"%s\")\n",
429 		       idn__debug_ucs4xstring(to, 50)));
430 	} else {
431 		TRACE(("idn_mapper_map(): %s\n", idn_result_tostring(r)));
432 	}
433 	return (r);
434 }
435 
436 idn_result_t
idn_mapper_register(const char * prefix,idn_mapper_createproc_t create,idn_mapper_destroyproc_t destroy,idn_mapper_mapproc_t map)437 idn_mapper_register(const char *prefix,
438 		    idn_mapper_createproc_t create,
439 		    idn_mapper_destroyproc_t destroy,
440 		    idn_mapper_mapproc_t map) {
441 	idn_result_t r;
442 	map_scheme_t *scheme = NULL;
443 
444 	assert(scheme_hash != NULL);
445 	assert(prefix != NULL && create != NULL && destroy != NULL &&
446 		map != NULL);
447 
448 	TRACE(("idn_mapper_register(prefix=%s)\n", prefix));
449 
450 	scheme = (map_scheme_t *) malloc(sizeof(map_scheme_t));
451 	if (scheme == NULL) {
452 		r = idn_nomemory;
453 		goto ret;
454 	}
455 
456 	scheme->prefix = (char *) malloc(strlen(prefix) + 1);
457 	if (scheme->prefix == NULL) {
458 		r = idn_nomemory;
459 		goto ret;
460 	}
461 
462 	strcpy(scheme->prefix, prefix);
463 	scheme->parameter = NULL;
464 	scheme->create    = create;
465 	scheme->destroy   = destroy;
466 	scheme->map       = map;
467 
468 	r = idn__strhash_put(scheme_hash, prefix, scheme);
469 	if (r != idn_success)
470 		goto ret;
471 
472 	r = idn_success;
473 ret:
474 	if (r != idn_success) {
475 		if (scheme != NULL)
476 			free(scheme->prefix);
477 		free(scheme);
478 	}
479 
480 	TRACE(("idn_mapper_register(): %s\n", idn_result_tostring(r)));
481 	return (r);
482 }
483