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