1 #ifndef lint
2 static char *rcsid = "$Id: checker.c,v 1.1 2003/06/04 00:25:49 marka Exp $";
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/checker.h>
58 #include <idn/strhash.h>
59 #include <idn/debug.h>
60 
61 /*
62  * Type for checking scheme.
63  */
64 typedef struct {
65 	char *prefix;
66 	char *parameter;
67 	idn_checker_createproc_t create;
68 	idn_checker_destroyproc_t destroy;
69 	idn_checker_lookupproc_t lookup;
70 	void *context;
71 } check_scheme_t;
72 
73 /*
74  * Standard checking schemes.
75  */
76 static const check_scheme_t rfc3491_prohibit_scheme = {
77 	"prohibit#RFC3491",
78 	"RFC3491",
79 	idn_nameprep_createproc,
80 	idn_nameprep_destroyproc,
81 	idn_nameprep_prohibitproc,
82 	NULL,
83 };
84 
85 static const check_scheme_t rfc3491_unasigned_scheme = {
86 	"unassigned#RFC3491",
87 	"RFC3491",
88 	idn_nameprep_createproc,
89 	idn_nameprep_destroyproc,
90 	idn_nameprep_unassignedproc,
91 	NULL,
92 };
93 
94 static const check_scheme_t rfc3491_bidi_scheme = {
95 	"bidi#RFC3491",
96 	"RFC3491",
97 	idn_nameprep_createproc,
98 	idn_nameprep_destroyproc,
99 	idn_nameprep_bidiproc,
100 	NULL,
101 };
102 
103 static const check_scheme_t filecheck_prohibit_scheme = {
104 	"prohibit#fileset",
105 	NULL,
106 	idn__filechecker_createproc,
107 	idn__filechecker_destroyproc,
108 	idn__filechecker_lookupproc,
109 	NULL,
110 };
111 
112 static const check_scheme_t filecheck_unassigned_scheme = {
113 	"unassigned#fileset",
114 	NULL,
115 	idn__filechecker_createproc,
116 	idn__filechecker_destroyproc,
117 	idn__filechecker_lookupproc,
118 	NULL,
119 };
120 
121 static const check_scheme_t *standard_check_schemes[] = {
122 	&rfc3491_unasigned_scheme,
123 	&rfc3491_prohibit_scheme,
124 	&rfc3491_bidi_scheme,
125 	&filecheck_prohibit_scheme,
126 	&filecheck_unassigned_scheme,
127 	NULL,
128 };
129 
130 /*
131  * Hash table for checking schemes.
132  */
133 static idn__strhash_t scheme_hash = NULL;
134 
135 /*
136  * Mapper object type.
137  */
138 struct idn_checker {
139 	int nschemes;
140 	int scheme_size;
141 	check_scheme_t *schemes;
142 	int reference_count;
143 };
144 
145 #define MAPPER_INITIAL_SCHEME_SIZE	1
146 
147 idn_result_t
idn_checker_initialize(void)148 idn_checker_initialize(void) {
149 	idn_result_t r;
150 	check_scheme_t **scheme;
151 
152 	TRACE(("idn_checker_initialize()\n"));
153 
154 	if (scheme_hash != NULL) {
155 		r = idn_success;	/* already initialized */
156 		goto ret;
157 	}
158 
159 	r = idn__strhash_create(&scheme_hash);
160 	if (r != idn_success) {
161 		goto ret;
162 	}
163 
164 	for (scheme = (check_scheme_t **)standard_check_schemes;
165 		*scheme != NULL; scheme++) {
166 		r = idn__strhash_put(scheme_hash, (*scheme)->prefix, *scheme);
167 		if (r != idn_success)
168 			goto ret;
169 	}
170 
171 	r = idn_success;
172 ret:
173 	if (r != idn_success) {
174 		if (scheme_hash != NULL) {
175 			idn__strhash_destroy(scheme_hash, NULL);
176 			scheme_hash = NULL;
177 		}
178 	}
179 	TRACE(("idn_checker_initialize(): %s\n", idn_result_tostring(r)));
180 	return (r);
181 }
182 
183 idn_result_t
idn_checker_create(idn_checker_t * ctxp)184 idn_checker_create(idn_checker_t *ctxp) {
185 	idn_checker_t ctx = NULL;
186 	idn_result_t r;
187 
188 	assert(scheme_hash != NULL);
189 	assert(ctxp != NULL);
190 
191 	TRACE(("idn_checker_create()\n"));
192 
193 	ctx = (idn_checker_t) malloc(sizeof(struct idn_checker));
194 	if (ctx == NULL) {
195 		r = idn_nomemory;
196 		goto ret;
197 	}
198 
199 	ctx->schemes = (check_scheme_t *) malloc(sizeof(check_scheme_t)
200 		 * MAPPER_INITIAL_SCHEME_SIZE);
201 	if (ctx->schemes == NULL) {
202 		r = idn_nomemory;
203 		goto ret;
204 	}
205 
206 	ctx->nschemes = 0;
207 	ctx->scheme_size = MAPPER_INITIAL_SCHEME_SIZE;
208 	ctx->reference_count = 1;
209 	*ctxp = ctx;
210 	r = idn_success;
211 ret:
212 	if (r != idn_success) {
213 		if (ctx != NULL)
214 			free(ctx->schemes);
215 		free(ctx);
216 	}
217 	TRACE(("idn_checker_create(): %s\n", idn_result_tostring(r)));
218 	return (r);
219 }
220 
221 void
idn_checker_destroy(idn_checker_t ctx)222 idn_checker_destroy(idn_checker_t ctx) {
223 	int i;
224 
225 	assert(scheme_hash != NULL);
226 	assert(ctx != NULL);
227 
228 	TRACE(("idn_checker_destroy()\n"));
229 
230 	ctx->reference_count--;
231 	if (ctx->reference_count <= 0) {
232 		TRACE(("idn_checker_destroy(): the object is destroyed\n"));
233 		for (i = 0; i < ctx->nschemes; i++)
234 			ctx->schemes[i].destroy(ctx->schemes[i].context);
235 		free(ctx->schemes);
236 		free(ctx);
237 	} else {
238 		TRACE(("idn_checker_destroy(): "
239 		       "update reference count (%d->%d)\n",
240 		       ctx->reference_count + 1, ctx->reference_count));
241 	}
242 }
243 
244 void
idn_checker_incrref(idn_checker_t ctx)245 idn_checker_incrref(idn_checker_t ctx) {
246 	assert(ctx != NULL && scheme_hash != NULL);
247 
248 	TRACE(("idn_checker_incrref()\n"));
249 	TRACE(("idn_checker_incrref: update reference count (%d->%d)\n",
250 		ctx->reference_count, ctx->reference_count + 1));
251 
252 	ctx->reference_count++;
253 }
254 
255 idn_result_t
idn_checker_add(idn_checker_t ctx,const char * scheme_name)256 idn_checker_add(idn_checker_t ctx, const char *scheme_name) {
257 	idn_result_t r;
258 	check_scheme_t *scheme;
259 	const char *scheme_prefix;
260 	const char *scheme_parameter;
261 	void *scheme_context = NULL;
262 	char *buffer = NULL;
263 
264 	assert(scheme_hash != NULL);
265 	assert(ctx != NULL);
266 
267 	TRACE(("idn_checker_add(scheme_name=%s)\n",
268 		idn__debug_xstring(scheme_name, 50)));
269 
270 	/*
271 	 * Split `scheme_name' into `scheme_prefix' and `scheme_parameter'.
272 	 */
273 	scheme_parameter = strchr(scheme_name, ':');
274 	if (scheme_parameter == NULL) {
275 		scheme_prefix = scheme_name;
276 		scheme_parameter = NULL;
277 	} else {
278 		ptrdiff_t scheme_prefixlen;
279 
280 		scheme_prefixlen = scheme_parameter - scheme_name;
281 		buffer = (char *) malloc(scheme_prefixlen + 1);
282 		if (buffer == NULL) {
283 			r = idn_nomemory;
284 			goto ret;
285 		}
286 		memcpy(buffer, scheme_name, scheme_prefixlen);
287 		*(buffer + scheme_prefixlen) = '\0';
288 		scheme_prefix = buffer;
289 		scheme_parameter++;
290 	}
291 
292 	/*
293 	 * Find a scheme.
294 	 */
295 	if (idn__strhash_get(scheme_hash, scheme_prefix, (void **)&scheme)
296 		!= idn_success) {
297 		ERROR(("idn_checker_add(): invalid scheme \"%-.30s\"\n",
298 		       scheme_name));
299 		r = idn_invalid_name;
300 		goto ret;
301 	}
302 	if (scheme_parameter == NULL && scheme->parameter != NULL)
303 		scheme_parameter = scheme->parameter;
304 
305 	/*
306 	 * Add the scheme.
307 	 */
308 	assert(ctx->nschemes <= ctx->scheme_size);
309 
310 	if (ctx->nschemes == ctx->scheme_size) {
311 		check_scheme_t *new_schemes;
312 
313 		new_schemes = (check_scheme_t *) realloc(ctx->schemes,
314 			sizeof(check_scheme_t) * ctx->scheme_size * 2);
315 		if (new_schemes == NULL) {
316 			r = idn_nomemory;
317 			goto ret;
318 		}
319 		ctx->schemes = new_schemes;
320 		ctx->scheme_size *= 2;
321 	}
322 
323 	r = scheme->create(scheme_parameter, &scheme_context);
324 	if (r != idn_success)
325 		goto ret;
326 
327 	memcpy(ctx->schemes + ctx->nschemes, scheme, sizeof(check_scheme_t));
328 	ctx->schemes[ctx->nschemes].context = scheme_context;
329 	ctx->nschemes++;
330 	r = idn_success;
331 
332 ret:
333 	free(buffer);
334 	if (r != idn_success)
335 		free(scheme_context);
336 	TRACE(("idn_checker_add(): %s\n", idn_result_tostring(r)));
337 	return (r);
338 }
339 
340 idn_result_t
idn_checker_addall(idn_checker_t ctx,const char ** scheme_names,int nschemes)341 idn_checker_addall(idn_checker_t ctx, const char **scheme_names,
342 		   int nschemes) {
343 	idn_result_t r;
344 	int i;
345 
346 	assert(scheme_hash != NULL);
347 	assert(ctx != NULL && scheme_names != NULL);
348 
349 	TRACE(("idn_checker_addall(nschemes=%d)\n", nschemes));
350 
351 	for (i = 0; i < nschemes; i++) {
352 		r = idn_checker_add(ctx, (const char *)*scheme_names);
353 		if (r != idn_success)
354 			goto ret;
355 		scheme_names++;
356 	}
357 
358 	r = idn_success;
359 ret:
360 	TRACE(("idn_checker_addall(): %s\n", idn_result_tostring(r)));
361 	return (r);
362 }
363 
364 idn_result_t
idn_checker_lookup(idn_checker_t ctx,const unsigned long * ucs4,const unsigned long ** found)365 idn_checker_lookup(idn_checker_t ctx, const unsigned long *ucs4,
366 		   const unsigned long **found) {
367 	idn_result_t r;
368 	int i;
369 
370 	assert(scheme_hash != NULL);
371 	assert(ctx != NULL && ucs4 != NULL && found != NULL);
372 
373 	TRACE(("idn_checker_lookup(ucs4=\"%s\")\n",
374 		idn__debug_ucs4xstring(ucs4, 50)));
375 
376 	/*
377 	 * Lookup.
378 	 */
379 	*found = NULL;
380 
381 	for (i = 0; i < ctx->nschemes; i++) {
382 		TRACE(("idn_checker_lookup(): lookup %s\n",
383 		       ctx->schemes[i].prefix));
384 
385 		r = (ctx->schemes[i].lookup)(ctx->schemes[i].context, ucs4,
386 					     found);
387 		if (r != idn_success)
388 			goto ret;
389 		if (*found != NULL)
390 			break;
391 	}
392 
393 	r = idn_success;
394 ret:
395 	if (*found == NULL) {
396 		TRACE(("idn_checker_lookup(): %s (not found)\n",
397 		       idn_result_tostring(r)));
398 	} else {
399 		TRACE(("idn_checker_lookup(): %s (found \\x%04lx)\n",
400 		       idn_result_tostring(r), **found));
401 	}
402 	return (r);
403 }
404 
405 idn_result_t
idn_checker_register(const char * prefix,idn_checker_createproc_t create,idn_checker_destroyproc_t destroy,idn_checker_lookupproc_t lookup)406 idn_checker_register(const char *prefix,
407 		    idn_checker_createproc_t create,
408 		    idn_checker_destroyproc_t destroy,
409 		    idn_checker_lookupproc_t lookup) {
410 	idn_result_t r;
411 	check_scheme_t *scheme = NULL;
412 
413 	assert(scheme_hash != NULL);
414 	assert(prefix != NULL && create != NULL && destroy != NULL &&
415 		lookup != NULL);
416 
417 	TRACE(("idn_checker_register(prefix=%s)\n", prefix));
418 
419 	scheme = (check_scheme_t *) malloc(sizeof(check_scheme_t));
420 	if (scheme == NULL) {
421 		r = idn_nomemory;
422 		goto ret;
423 	}
424 
425 	scheme->prefix = (char *) malloc(strlen(prefix) + 1);
426 	if (scheme->prefix == NULL) {
427 		r = idn_nomemory;
428 		goto ret;
429 	}
430 
431 	strcpy(scheme->prefix, prefix);
432 	scheme->parameter = NULL;
433 	scheme->create    = create;
434 	scheme->destroy   = destroy;
435 	scheme->lookup    = lookup;
436 
437 	r = idn__strhash_put(scheme_hash, prefix, scheme);
438 ret:
439 	if (r != idn_success) {
440 		if (scheme != NULL)
441 			free(scheme->prefix);
442 		free(scheme);
443 	}
444 	TRACE(("idn_checker_register(): %s\n", idn_result_tostring(r)));
445 	return (r);
446 }
447