diff options
Diffstat (limited to 'source3/lib/iconv.c')
-rw-r--r-- | source3/lib/iconv.c | 204 |
1 files changed, 140 insertions, 64 deletions
diff --git a/source3/lib/iconv.c b/source3/lib/iconv.c index e54a74864e..43350d9349 100644 --- a/source3/lib/iconv.c +++ b/source3/lib/iconv.c @@ -2,7 +2,6 @@ Unix SMB/CIFS implementation. minimal iconv implementation Copyright (C) Andrew Tridgell 2001 - Copyright (C) Jelmer Vernooij 2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,51 +24,31 @@ static size_t ascii_pull(void *,char **, size_t *, char **, size_t *); static size_t ascii_push(void *,char **, size_t *, char **, size_t *); static size_t utf8_pull(void *,char **, size_t *, char **, size_t *); static size_t utf8_push(void *,char **, size_t *, char **, size_t *); +static size_t weird_pull(void *,char **, size_t *, char **, size_t *); +static size_t weird_push(void *,char **, size_t *, char **, size_t *); static size_t ucs2hex_pull(void *,char **, size_t *, char **, size_t *); static size_t ucs2hex_push(void *,char **, size_t *, char **, size_t *); static size_t iconv_copy(void *,char **, size_t *, char **, size_t *); -struct charset_functions builtin_functions[] = { - {"UCS-2LE", iconv_copy, iconv_copy}, - {"UTF8", utf8_pull, utf8_push}, - {"ASCII", ascii_pull, ascii_push}, - {"UCS2-HEX", ucs2hex_pull, ucs2hex_push}, - {NULL, NULL, NULL} +/* + for each charset we have a function that pulls from that charset to + a ucs2 buffer, and a function that pushes to a ucs2 buffer +*/ +static struct { + char *name; + size_t (*pull)(void *, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + size_t (*push)(void *, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +} charsets[] = { + {"UCS-2LE", iconv_copy, iconv_copy}, + {"UTF8", utf8_pull, utf8_push}, + {"ASCII", ascii_pull, ascii_push}, + {"WEIRD", weird_pull, weird_push}, + {"UCS2-HEX", ucs2hex_pull, ucs2hex_push}, + {NULL, NULL, NULL} }; -static struct charset_functions *charsets = NULL; - -BOOL smb_register_charset(struct charset_functions *funcs) -{ - struct charset_functions *c = charsets; - - DEBUG(5, ("Attempting to register new charset %s\n", funcs->name)); - /* Check whether we already have this charset... */ - while(c) { - if(!strcasecmp(c->name, funcs->name)){ - DEBUG(2, ("Duplicate charset %s, not registering\n", funcs->name)); - return False; - } - c = c->next; - } - - funcs->next = funcs->prev = NULL; - DEBUG(5, ("Registered charset %s\n", funcs->name)); - DLIST_ADD(charsets, funcs); - return True; -} - -void lazy_initialize_iconv(void) -{ - static BOOL initialized = False; - int i; - - if (!initialized) { - initialized = True; - for(i = 0; builtin_functions[i].name; i++) - smb_register_charset(&builtin_functions[i]); - } -} /* if there was an error then reset the internal state, this ensures that we don't have a shift state remaining for @@ -136,11 +115,7 @@ size_t smb_iconv(smb_iconv_t cd, smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) { smb_iconv_t ret; - struct charset_functions *from, *to; - - lazy_initialize_iconv(); - from = charsets; - to = charsets; + int from, to; ret = (smb_iconv_t)malloc(sizeof(*ret)); if (!ret) { @@ -158,52 +133,48 @@ smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) return ret; } - while (from) { - if (strcasecmp(from->name, fromcode) == 0) break; - from = from->next; + for (from=0; charsets[from].name; from++) { + if (strcasecmp(charsets[from].name, fromcode) == 0) break; } - - while (to) { - if (strcasecmp(to->name, tocode) == 0) break; - to = to->next; + for (to=0; charsets[to].name; to++) { + if (strcasecmp(charsets[to].name, tocode) == 0) break; } #ifdef HAVE_NATIVE_ICONV - if (!from) { + if (!charsets[from].name) { ret->pull = sys_iconv; ret->cd_pull = iconv_open("UCS-2LE", fromcode); if (ret->cd_pull == (iconv_t)-1) goto failed; } - - if (!to) { + if (!charsets[to].name) { ret->push = sys_iconv; ret->cd_push = iconv_open(tocode, "UCS-2LE"); if (ret->cd_push == (iconv_t)-1) goto failed; } #else - if (!from || !to) { + if (!charsets[from].name || !charsets[to].name) { goto failed; } #endif /* check for conversion to/from ucs2 */ - if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) { - ret->direct = to->push; + if (from == 0 && charsets[to].name) { + ret->direct = charsets[to].push; return ret; } - if (strcasecmp(tocode, "UCS-2LE") == 0 && from) { - ret->direct = from->pull; + if (to == 0 && charsets[from].name) { + ret->direct = charsets[from].pull; return ret; } #ifdef HAVE_NATIVE_ICONV - if (strcasecmp(fromcode, "UCS-2LE") == 0) { + if (from == 0) { ret->direct = sys_iconv; ret->cd_direct = ret->cd_push; ret->cd_push = NULL; return ret; } - if (strcasecmp(tocode, "UCS-2LE") == 0) { + if (to == 0) { ret->direct = sys_iconv; ret->cd_direct = ret->cd_pull; ret->cd_pull = NULL; @@ -212,8 +183,8 @@ smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) #endif /* the general case has to go via a buffer */ - if (!ret->pull) ret->pull = from->pull; - if (!ret->push) ret->push = to->push; + if (!ret->pull) ret->pull = charsets[from].pull; + if (!ret->push) ret->push = charsets[to].push; return ret; failed: @@ -381,6 +352,111 @@ static size_t ucs2hex_push(void *cd, char **inbuf, size_t *inbytesleft, } +/* the "weird" character set is very useful for testing multi-byte + support and finding bugs. Don't use on a production system! +*/ +static struct { + char from; + char *to; + int len; +} weird_table[] = { + {'q', "^q^", 3}, + {'Q', "^Q^", 3}, + {0, NULL} +}; + +static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + int i; + int done = 0; + for (i=0;weird_table[i].from;i++) { + if (strncmp((*inbuf), + weird_table[i].to, + weird_table[i].len) == 0) { + if (*inbytesleft < weird_table[i].len) { + DEBUG(0,("ERROR: truncated weird string\n")); + /* smb_panic("weird_pull"); */ + + } else { + (*outbuf)[0] = weird_table[i].from; + (*outbuf)[1] = 0; + (*inbytesleft) -= weird_table[i].len; + (*outbytesleft) -= 2; + (*inbuf) += weird_table[i].len; + (*outbuf) += 2; + done = 1; + break; + } + } + } + if (done) continue; + (*outbuf)[0] = (*inbuf)[0]; + (*outbuf)[1] = 0; + (*inbytesleft) -= 1; + (*outbytesleft) -= 2; + (*inbuf) += 1; + (*outbuf) += 2; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return 0; +} + +static size_t weird_push(void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + int ir_count=0; + + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + int i; + int done=0; + for (i=0;weird_table[i].from;i++) { + if ((*inbuf)[0] == weird_table[i].from && + (*inbuf)[1] == 0) { + if (*outbytesleft < weird_table[i].len) { + DEBUG(0,("No room for weird character\n")); + /* smb_panic("weird_push"); */ + } else { + memcpy(*outbuf, weird_table[i].to, + weird_table[i].len); + (*inbytesleft) -= 2; + (*outbytesleft) -= weird_table[i].len; + (*inbuf) += 2; + (*outbuf) += weird_table[i].len; + done = 1; + break; + } + } + } + if (done) continue; + + (*outbuf)[0] = (*inbuf)[0]; + if ((*inbuf)[1]) ir_count++; + (*inbytesleft) -= 2; + (*outbytesleft) -= 1; + (*inbuf) += 2; + (*outbuf) += 1; + } + + if (*inbytesleft == 1) { + errno = EINVAL; + return -1; + } + + if (*inbytesleft > 1) { + errno = E2BIG; + return -1; + } + + return ir_count; +} + static size_t iconv_copy(void *cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { |