1#ifndef lint 2static char *rcsid = "$Id: mapper.c,v 1.1 2003/06/04 00:25:55 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/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 */ 66typedef 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 */ 78static 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 87static 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 96static 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 */ 105static idn__strhash_t scheme_hash = NULL; 106 107/* 108 * Mapper object type. 109 */ 110struct 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 119idn_result_t 120idn_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; 143ret: 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 152idn_result_t 153idn_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 181ret: 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 191void 192idn_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 214void 215idn_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 225idn_result_t 226idn_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; 307ret: 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 316idn_result_t 317idn_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; 334ret: 335 TRACE(("idn_mapper_addall(): %s\n", idn_result_tostring(r))); 336 return (r); 337} 338 339idn_result_t 340idn_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; 424ret: 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 436idn_result_t 437idn_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; 473ret: 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