1#ifndef lint
2static 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 */
64typedef 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 */
76static 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
85static 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
94static 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
103static 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
112static 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
121static 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 */
133static idn__strhash_t scheme_hash = NULL;
134
135/*
136 * Mapper object type.
137 */
138struct 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
147idn_result_t
148idn_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;
172ret:
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
183idn_result_t
184idn_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;
211ret:
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
221void
222idn_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
244void
245idn_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
255idn_result_t
256idn_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
332ret:
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
340idn_result_t
341idn_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;
359ret:
360	TRACE(("idn_checker_addall(): %s\n", idn_result_tostring(r)));
361	return (r);
362}
363
364idn_result_t
365idn_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;
394ret:
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
405idn_result_t
406idn_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);
438ret:
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