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