1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apu.h" 18#include "apu_config.h" 19#include "apr_lib.h" 20#include "apr_strings.h" 21#include "apr_portable.h" 22#include "apr_xlate.h" 23 24/* If no implementation is available, don't generate code here since 25 * apr_xlate.h emitted macros which return APR_ENOTIMPL. 26 */ 27 28#if APR_HAS_XLATE 29 30#ifdef HAVE_STDDEF_H 31#include <stddef.h> /* for NULL */ 32#endif 33#if APR_HAVE_STRING_H 34#include <string.h> 35#endif 36#if APR_HAVE_STRINGS_H 37#include <strings.h> 38#endif 39#ifdef HAVE_ICONV_H 40#include <iconv.h> 41#endif 42#if APU_HAVE_APR_ICONV 43#include <apr_iconv.h> 44#endif 45 46#if defined(APU_ICONV_INBUF_CONST) || APU_HAVE_APR_ICONV 47#define ICONV_INBUF_TYPE const char ** 48#else 49#define ICONV_INBUF_TYPE char ** 50#endif 51 52#ifndef min 53#define min(x,y) ((x) <= (y) ? (x) : (y)) 54#endif 55 56struct apr_xlate_t { 57 apr_pool_t *pool; 58 char *frompage; 59 char *topage; 60 char *sbcs_table; 61#if APU_HAVE_ICONV 62 iconv_t ich; 63#elif APU_HAVE_APR_ICONV 64 apr_iconv_t ich; 65#endif 66}; 67 68 69static const char *handle_special_names(const char *page, apr_pool_t *pool) 70{ 71 if (page == APR_DEFAULT_CHARSET) { 72 return apr_os_default_encoding(pool); 73 } 74 else if (page == APR_LOCALE_CHARSET) { 75 return apr_os_locale_encoding(pool); 76 } 77 else { 78 return page; 79 } 80} 81 82static apr_status_t apr_xlate_cleanup(void *convset) 83{ 84 apr_xlate_t *old = convset; 85 86#if APU_HAVE_APR_ICONV 87 if (old->ich != (apr_iconv_t)-1) { 88 return apr_iconv_close(old->ich, old->pool); 89 } 90 91#elif APU_HAVE_ICONV 92 if (old->ich != (iconv_t)-1) { 93 if (iconv_close(old->ich)) { 94 int rv = errno; 95 96 /* Sometimes, iconv is not good about setting errno. */ 97 return rv ? rv : APR_EINVAL; 98 } 99 } 100#endif 101 102 return APR_SUCCESS; 103} 104 105#if APU_HAVE_ICONV 106static void check_sbcs(apr_xlate_t *convset) 107{ 108 char inbuf[256], outbuf[256]; 109 char *inbufptr = inbuf; 110 char *outbufptr = outbuf; 111 apr_size_t inbytes_left, outbytes_left; 112 int i; 113 apr_size_t translated; 114 115 for (i = 0; i < sizeof(inbuf); i++) { 116 inbuf[i] = i; 117 } 118 119 inbytes_left = outbytes_left = sizeof(inbuf); 120 translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 121 &inbytes_left, &outbufptr, &outbytes_left); 122 123 if (translated != (apr_size_t)-1 124 && inbytes_left == 0 125 && outbytes_left == 0) { 126 /* hurray... this is simple translation; save the table, 127 * close the iconv descriptor 128 */ 129 130 convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf)); 131 memcpy(convset->sbcs_table, outbuf, sizeof(outbuf)); 132 iconv_close(convset->ich); 133 convset->ich = (iconv_t)-1; 134 135 /* TODO: add the table to the cache */ 136 } 137 else { 138 /* reset the iconv descriptor, since it's now in an undefined 139 * state. */ 140 iconv_close(convset->ich); 141 convset->ich = iconv_open(convset->topage, convset->frompage); 142 } 143} 144#elif APU_HAVE_APR_ICONV 145static void check_sbcs(apr_xlate_t *convset) 146{ 147 char inbuf[256], outbuf[256]; 148 char *inbufptr = inbuf; 149 char *outbufptr = outbuf; 150 apr_size_t inbytes_left, outbytes_left; 151 int i; 152 apr_size_t translated; 153 apr_status_t rv; 154 155 for (i = 0; i < sizeof(inbuf); i++) { 156 inbuf[i] = i; 157 } 158 159 inbytes_left = outbytes_left = sizeof(inbuf); 160 rv = apr_iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 161 &inbytes_left, &outbufptr, &outbytes_left, 162 &translated); 163 164 if ((rv == APR_SUCCESS) 165 && (translated != (apr_size_t)-1) 166 && inbytes_left == 0 167 && outbytes_left == 0) { 168 /* hurray... this is simple translation; save the table, 169 * close the iconv descriptor 170 */ 171 172 convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf)); 173 memcpy(convset->sbcs_table, outbuf, sizeof(outbuf)); 174 apr_iconv_close(convset->ich, convset->pool); 175 convset->ich = (apr_iconv_t)-1; 176 177 /* TODO: add the table to the cache */ 178 } 179 else { 180 /* reset the iconv descriptor, since it's now in an undefined 181 * state. */ 182 apr_iconv_close(convset->ich, convset->pool); 183 rv = apr_iconv_open(convset->topage, convset->frompage, 184 convset->pool, &convset->ich); 185 } 186} 187#endif /* APU_HAVE_APR_ICONV */ 188 189static void make_identity_table(apr_xlate_t *convset) 190{ 191 int i; 192 193 convset->sbcs_table = apr_palloc(convset->pool, 256); 194 for (i = 0; i < 256; i++) 195 convset->sbcs_table[i] = i; 196} 197 198APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, 199 const char *topage, 200 const char *frompage, 201 apr_pool_t *pool) 202{ 203 apr_status_t rv; 204 apr_xlate_t *new; 205 int found = 0; 206 207 *convset = NULL; 208 209 topage = handle_special_names(topage, pool); 210 frompage = handle_special_names(frompage, pool); 211 212 new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t)); 213 if (!new) { 214 return APR_ENOMEM; 215 } 216 217 new->pool = pool; 218 new->topage = apr_pstrdup(pool, topage); 219 new->frompage = apr_pstrdup(pool, frompage); 220 if (!new->topage || !new->frompage) { 221 return APR_ENOMEM; 222 } 223 224#ifdef TODO 225 /* search cache of codepage pairs; we may be able to avoid the 226 * expensive iconv_open() 227 */ 228 229 set found to non-zero if found in the cache 230#endif 231 232 if ((! found) && (strcmp(topage, frompage) == 0)) { 233 /* to and from are the same */ 234 found = 1; 235 make_identity_table(new); 236 } 237 238#if APU_HAVE_APR_ICONV 239 if (!found) { 240 rv = apr_iconv_open(topage, frompage, pool, &new->ich); 241 if (rv != APR_SUCCESS) { 242 return rv; 243 } 244 found = 1; 245 check_sbcs(new); 246 } else 247 new->ich = (apr_iconv_t)-1; 248 249#elif APU_HAVE_ICONV 250 if (!found) { 251 new->ich = iconv_open(topage, frompage); 252 if (new->ich == (iconv_t)-1) { 253 int rv = errno; 254 /* Sometimes, iconv is not good about setting errno. */ 255 return rv ? rv : APR_EINVAL; 256 } 257 found = 1; 258 check_sbcs(new); 259 } else 260 new->ich = (iconv_t)-1; 261#endif /* APU_HAVE_ICONV */ 262 263 if (found) { 264 *convset = new; 265 apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup, 266 apr_pool_cleanup_null); 267 rv = APR_SUCCESS; 268 } 269 else { 270 rv = APR_EINVAL; /* iconv() would return EINVAL if it 271 couldn't handle the pair */ 272 } 273 274 return rv; 275} 276 277APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) 278{ 279 *onoff = convset->sbcs_table != NULL; 280 return APR_SUCCESS; 281} 282 283APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, 284 const char *inbuf, 285 apr_size_t *inbytes_left, 286 char *outbuf, 287 apr_size_t *outbytes_left) 288{ 289 apr_status_t status = APR_SUCCESS; 290 291#if APU_HAVE_APR_ICONV 292 if (convset->ich != (apr_iconv_t)-1) { 293 const char *inbufptr = inbuf; 294 apr_size_t translated; 295 char *outbufptr = outbuf; 296 status = apr_iconv(convset->ich, &inbufptr, inbytes_left, 297 &outbufptr, outbytes_left, &translated); 298 299 /* If everything went fine but we ran out of buffer, don't 300 * report it as an error. Caller needs to look at the two 301 * bytes-left values anyway. 302 * 303 * There are three expected cases where rc is -1. In each of 304 * these cases, *inbytes_left != 0. 305 * a) the non-error condition where we ran out of output 306 * buffer 307 * b) the non-error condition where we ran out of input (i.e., 308 * the last input character is incomplete) 309 * c) the error condition where the input is invalid 310 */ 311 switch (status) { 312 313 case APR_BADARG: /* out of space on output */ 314 status = 0; /* change table lookup code below if you 315 make this an error */ 316 break; 317 318 case APR_EINVAL: /* input character not complete (yet) */ 319 status = APR_INCOMPLETE; 320 break; 321 322 case APR_BADCH: /* bad input byte */ 323 status = APR_EINVAL; 324 break; 325 326 /* Sometimes, iconv is not good about setting errno. */ 327 case 0: 328 if (inbytes_left && *inbytes_left) 329 status = APR_INCOMPLETE; 330 break; 331 332 default: 333 break; 334 } 335 } 336 else 337 338#elif APU_HAVE_ICONV 339 if (convset->ich != (iconv_t)-1) { 340 const char *inbufptr = inbuf; 341 char *outbufptr = outbuf; 342 apr_size_t translated; 343 translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 344 inbytes_left, &outbufptr, outbytes_left); 345 346 /* If everything went fine but we ran out of buffer, don't 347 * report it as an error. Caller needs to look at the two 348 * bytes-left values anyway. 349 * 350 * There are three expected cases where rc is -1. In each of 351 * these cases, *inbytes_left != 0. 352 * a) the non-error condition where we ran out of output 353 * buffer 354 * b) the non-error condition where we ran out of input (i.e., 355 * the last input character is incomplete) 356 * c) the error condition where the input is invalid 357 */ 358 if (translated == (apr_size_t)-1) { 359 int rv = errno; 360 switch (rv) { 361 362 case E2BIG: /* out of space on output */ 363 status = 0; /* change table lookup code below if you 364 make this an error */ 365 break; 366 367 case EINVAL: /* input character not complete (yet) */ 368 status = APR_INCOMPLETE; 369 break; 370 371 case EILSEQ: /* bad input byte */ 372 status = APR_EINVAL; 373 break; 374 375 /* Sometimes, iconv is not good about setting errno. */ 376 case 0: 377 status = APR_INCOMPLETE; 378 break; 379 380 default: 381 status = rv; 382 break; 383 } 384 } 385 } 386 else 387#endif 388 389 if (inbuf) { 390 apr_size_t to_convert = min(*inbytes_left, *outbytes_left); 391 apr_size_t converted = to_convert; 392 char *table = convset->sbcs_table; 393 394 while (to_convert) { 395 *outbuf = table[(unsigned char)*inbuf]; 396 ++outbuf; 397 ++inbuf; 398 --to_convert; 399 } 400 *inbytes_left -= converted; 401 *outbytes_left -= converted; 402 } 403 404 return status; 405} 406 407APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, 408 unsigned char inchar) 409{ 410 if (convset->sbcs_table) { 411 return convset->sbcs_table[inchar]; 412 } 413 else { 414 return -1; 415 } 416} 417 418APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) 419{ 420 return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup); 421} 422 423#else /* !APR_HAS_XLATE */ 424 425APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, 426 const char *topage, 427 const char *frompage, 428 apr_pool_t *pool) 429{ 430 return APR_ENOTIMPL; 431} 432 433APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) 434{ 435 return APR_ENOTIMPL; 436} 437 438APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, 439 unsigned char inchar) 440{ 441 return (-1); 442} 443 444APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, 445 const char *inbuf, 446 apr_size_t *inbytes_left, 447 char *outbuf, 448 apr_size_t *outbytes_left) 449{ 450 return APR_ENOTIMPL; 451} 452 453APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) 454{ 455 return APR_ENOTIMPL; 456} 457 458#endif /* APR_HAS_XLATE */ 459