diff options
-rw-r--r-- | camel/ChangeLog | 7 | ||||
-rw-r--r-- | camel/camel-mime-filter-charset.c | 196 |
2 files changed, 115 insertions, 88 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index a58e1b2017..b7972266b9 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,10 @@ +2002-09-23 Jeffrey Stedfast <fejj@ximian.com> + + * camel-mime-filter-charset.c (complete): Handle EILSEQ by eating + bytes in the input buffer until conversion is successful. The old + behaviour aborted when it got EILSEQ and just dumped the raw data. + (filter): Same. Fixes bug #30873. + 2002-09-24 Not Zed <NotZed@Ximian.com> * camel-mime-utils.c (mail_list_magic[]): Changed the Mailing-List diff --git a/camel/camel-mime-filter-charset.c b/camel/camel-mime-filter-charset.c index f30047c96a..28c4af4db4 100644 --- a/camel/camel-mime-filter-charset.c +++ b/camel/camel-mime-filter-charset.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2000 Ximian Inc. * @@ -84,119 +85,138 @@ reset(CamelMimeFilter *mf) } static void -complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace) +complete(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace) { - CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)mf; - size_t converted, inlen, outlen; + CamelMimeFilterCharset *charset = (CamelMimeFilterCharset *)mf; + size_t inleft, outleft, converted = 0; const char *inbuf; char *outbuf; - if (f->ic == (iconv_t) -1) { - goto donothing; - } - - /* FIXME: there's probably a safer way to size this ...? */ - /* We could always resize if we run out of room in outbuf (but it'd be nice not - to have to) */ - camel_mime_filter_set_size(mf, len*5+16, FALSE); - inbuf = in; - inlen = len; + if (charset->ic == (iconv_t) -1) + goto noop; + + camel_mime_filter_set_size (mf, len * 5 + 16, FALSE); outbuf = mf->outbuf; - outlen = mf->outsize; - - /* temporary fix to find another bug somewhere */ - d(memset(outbuf, 0, outlen)); - - if (inlen>0) { - converted = e_iconv(f->ic, &inbuf, &inlen, &outbuf, &outlen); - if (converted == (size_t) -1) { - if (errno != EINVAL) { - g_warning("error occured converting: %s", strerror(errno)); - goto donothing; + outleft = mf->outsize; + + inbuf = in; + inleft = len; + + if (inleft > 0) { + do { + converted = e_iconv (charset->ic, &inbuf, &inleft, &outbuf, &outleft); + if (converted == (size_t) -1) { + if (errno == E2BIG) { + /* + * E2BIG There is not sufficient room at *outbuf. + * + * We just need to grow our outbuffer and try again. + */ + + converted = outbuf - mf->outbuf; + camel_mime_filter_set_size (mf, inleft * 5 + mf->outsize + 16, TRUE); + outbuf = mf->outbuf + converted; + outleft = mf->outsize - converted; + } else if (errno == EILSEQ) { + /* + * EILSEQ An invalid multibyte sequence has been encountered + * in the input. + * + * What we do here is eat the invalid bytes in the sequence and continue + */ + + inbuf++; + inleft--; + } else if (errno == EINVAL) { + /* + * EINVAL An incomplete multibyte sequence has been encoun + * tered in the input. + * + * We assume that this can only happen if we've run out of + * bytes for a multibyte sequence, if not we're in trouble. + */ + + break; + } else + goto noop; } - } - - if (inlen>0) { - g_warning("Output lost in character conversion, invalid sequence encountered?"); - } - } - - /* this 'resets' the output stream, returning back to the initial - shift state for multishift charactersets */ - converted = e_iconv(f->ic, NULL, 0, &outbuf, &outlen); - if (converted == (size_t) -1) { - g_warning("Conversion failed to complete: %s", strerror(errno)); + } while (((int) inleft) > 0); } - - /* debugging assertion - check for NUL's in output */ - d({ - int i; - - for (i=0;i<(mf->outsize - outlen);i++) { - g_assert(mf->outbuf[i]); - } - }); - + + /* flush the iconv conversion */ + e_iconv (charset->ic, NULL, NULL, &outbuf, &outleft); + *out = mf->outbuf; - *outlenptr = mf->outsize - outlen; + *outlen = mf->outsize - outleft; *outprespace = mf->outpre; + return; - -donothing: + + noop: + *out = in; - *outlenptr = len; + *outlen = len; *outprespace = prespace; } static void -filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlenptr, size_t *outprespace) +filter(CamelMimeFilter *mf, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace) { - CamelMimeFilterCharset *f = (CamelMimeFilterCharset *)mf; - size_t converted, inlen, outlen; + CamelMimeFilterCharset *charset = (CamelMimeFilterCharset *)mf; + size_t inleft, outleft, converted = 0; const char *inbuf; char *outbuf; - - if (f->ic == (iconv_t) -1) { - goto donothing; - } - - /* FIXME: there's probably a safer way to size this ...? */ - camel_mime_filter_set_size(mf, len*5+16, FALSE); + + if (charset->ic == (iconv_t) -1) + goto noop; + + camel_mime_filter_set_size (mf, len * 5 + 16, FALSE); + outbuf = mf->outbuf + converted; + outleft = mf->outsize - converted; + inbuf = in; - inlen = len; - outbuf = mf->outbuf; - outlen = mf->outsize; - converted = e_iconv(f->ic, &inbuf, &inlen, &outbuf, &outlen); - if (converted == (size_t) -1) { - if (errno != EINVAL) { - g_warning("error occured converting: %s", strerror(errno)); - goto donothing; + inleft = len; + + do { + converted = e_iconv (charset->ic, &inbuf, &inleft, &outbuf, &outleft); + if (converted == (size_t) -1) { + if (errno == E2BIG || errno == EINVAL) + break; + + if (errno == EILSEQ) { + /* + * EILSEQ An invalid multibyte sequence has been encountered + * in the input. + * + * What we do here is eat the invalid bytes in the sequence and continue + */ + + inbuf++; + inleft--; + } else { + /* unknown error condition */ + goto noop; + } } + } while (((int) inleft) > 0); + + if (((int) inleft) > 0) { + /* We've either got an E2BIG or EINVAL. Save the + remainder of the buffer as we'll process this next + time through */ + camel_mime_filter_backup (mf, inbuf, inleft); } - - /* - NOTE: This assumes EINVAL only occurs because we ran out of - bytes for a multibyte sequence, if not, we're in trouble. - */ - - /* This is to fix a bug in at least 1 version of glibc iconv: we get EINVAL and - it reads past the input and returns a converted length of -1 ... so discard - any overruns as failed */ - if (((int)inlen) < 0) - inlen = 0; - - if (inlen>0) { - camel_mime_filter_backup(mf, inbuf, inlen); - } - + *out = mf->outbuf; - *outlenptr = mf->outsize - outlen; + *outlen = outbuf - mf->outbuf; *outprespace = mf->outpre; + return; - -donothing: + + noop: + *out = in; - *outlenptr = len; + *outlen = len; *outprespace = prespace; } |