diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/lib/charcnv.c | 92 | ||||
-rw-r--r-- | source3/lib/iconv.c | 1 |
2 files changed, 82 insertions, 11 deletions
diff --git a/source3/lib/charcnv.c b/source3/lib/charcnv.c index 5f9392b620..5bd58e3031 100644 --- a/source3/lib/charcnv.c +++ b/source3/lib/charcnv.c @@ -184,14 +184,18 @@ static size_t convert_string_internal(charset_t from, charset_t to, } } + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { if (!conv_silent) DEBUG(0,("convert_string_internal: Conversion not supported.\n")); - goto use_as_is; + return 0; } i_len=srclen; o_len=destlen; + + again: + retval = smb_iconv(descriptor, (char **)&inbuf, &i_len, &outbuf, &o_len); if(retval==(size_t)-1) { const char *reason="unknown error"; @@ -227,12 +231,76 @@ static size_t convert_string_internal(charset_t from, charset_t to, use_as_is: - /* conversion not supported, use as is */ + /* + * Conversion not supported. This is actually an error, but there are so + * many misconfigured iconv systems and smb.conf's out there we can't just + * fail. Do a very bad conversion instead.... JRA. + */ + { - size_t len = MIN(srclen,destlen); - if (len) - memcpy(dest,src,len); - return len; + if (o_len == 0 || i_len == 0) + return destlen - o_len; + + if (from == CH_UCS2 && to != CH_UCS2) { + /* Can't convert from ucs2 to multibyte. Just truncate this char to ascii. */ + if (i_len < 2) + return destlen - o_len; + if (i_len >= 2) { + *outbuf = inbuf[0]; + + outbuf++; + o_len--; + + inbuf += 2; + i_len -= 2; + } + + if (o_len == 0 || i_len == 0) + return destlen - o_len; + + /* Keep trying with the next char... */ + goto again; + + } else if (from != CH_UCS2 && to == CH_UCS2) { + /* Can't convert to ucs2 - just widen by adding zero. */ + if (o_len < 2) + return destlen; + outbuf[0] = inbuf[0]; + outbuf[1] = '\0'; + + inbuf++; + i_len--; + + outbuf += 2; + o_len -= 2; + + if (o_len == 0 || i_len == 0) + return destlen - o_len; + + /* Keep trying with the next char... */ + goto again; + + } else if (from != CH_UCS2 && to != CH_UCS2) { + /* Failed multibyte to multibyte. Just copy 1 char and + try again. */ + outbuf[0] = inbuf[0]; + + inbuf++; + i_len--; + + outbuf++; + o_len--; + + if (o_len == 0 || i_len == 0) + return destlen - o_len; + + /* Keep trying with the next char... */ + goto again; + + } else { + /* Keep compiler happy.... */ + return destlen - o_len; + } } } @@ -241,9 +309,9 @@ static size_t convert_string_internal(charset_t from, charset_t to, * Fast path version - handles ASCII first. * * @param src pointer to source string (multibyte or singlebyte) - * @param srclen length of the source string in bytes + * @param srclen length of the source string in bytes, or -1 for nul terminated. * @param dest pointer to destination string (multibyte or singlebyte) - * @param destlen maximal length allowed for string + * @param destlen maximal length allowed for string - *NEVER* -1. * @returns the number of bytes occupied in the destination * * Ensure the srclen contains the terminating zero. @@ -257,11 +325,15 @@ size_t convert_string(charset_t from, charset_t to, void *dest, size_t destlen) { /* - * NB. We deliberately don't do a strlen here is srclen == -1. + * NB. We deliberately don't do a strlen here if srclen == -1. * This is very expensive over millions of calls and is taken * care of in the slow path in convert_string_internal. JRA. */ +#ifdef DEVELOPER + SMB_ASSERT(destlen != (size_t)-1); +#endif + if (srclen == 0) return 0; @@ -302,7 +374,7 @@ size_t convert_string(charset_t from, charset_t to, unsigned char lastp; /* If all characters are ascii, fast path here. */ - while ((slen >= 2) && dlen) { + while (((slen == (size_t)-1) || (slen >= 2)) && dlen) { if (((lastp = *p) <= 0x7f) && (p[1] == 0)) { *q++ = *p; if (slen != (size_t)-1) { diff --git a/source3/lib/iconv.c b/source3/lib/iconv.c index b0c13a5ee6..d0d2dcd1c4 100644 --- a/source3/lib/iconv.c +++ b/source3/lib/iconv.c @@ -589,4 +589,3 @@ toobig: errno = E2BIG; return -1; } - |