1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2000-2012 Jeffrey Stedfast
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
28 #include "gmime-filter-charset.h"
29 #include "gmime-charset.h"
30 #include "gmime-iconv.h"
34 * SECTION: gmime-filter-charset
35 * @title: GMimeFilterCharset
36 * @short_description: Charset-conversion filter
39 * A #GMimeFilter which is used for converting text from one charset
44 static void g_mime_filter_charset_class_init (GMimeFilterCharsetClass *klass);
45 static void g_mime_filter_charset_init (GMimeFilterCharset *filter, GMimeFilterCharsetClass *klass);
46 static void g_mime_filter_charset_finalize (GObject *object);
48 static GMimeFilter *filter_copy (GMimeFilter *filter);
49 static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
50 char **out, size_t *outlen, size_t *outprespace);
51 static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
52 char **out, size_t *outlen, size_t *outprespace);
53 static void filter_reset (GMimeFilter *filter);
56 static GMimeFilterClass *parent_class = NULL;
60 g_mime_filter_charset_get_type (void)
62 static GType type = 0;
65 static const GTypeInfo info = {
66 sizeof (GMimeFilterCharsetClass),
67 NULL, /* base_class_init */
68 NULL, /* base_class_finalize */
69 (GClassInitFunc) g_mime_filter_charset_class_init,
70 NULL, /* class_finalize */
71 NULL, /* class_data */
72 sizeof (GMimeFilterCharset),
74 (GInstanceInitFunc) g_mime_filter_charset_init,
77 type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterCharset", &info, 0);
85 g_mime_filter_charset_class_init (GMimeFilterCharsetClass *klass)
87 GObjectClass *object_class = G_OBJECT_CLASS (klass);
88 GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
90 parent_class = g_type_class_ref (GMIME_TYPE_FILTER);
92 object_class->finalize = g_mime_filter_charset_finalize;
94 filter_class->copy = filter_copy;
95 filter_class->filter = filter_filter;
96 filter_class->complete = filter_complete;
97 filter_class->reset = filter_reset;
101 g_mime_filter_charset_init (GMimeFilterCharset *filter, GMimeFilterCharsetClass *klass)
103 filter->from_charset = NULL;
104 filter->to_charset = NULL;
105 filter->cd = (iconv_t) -1;
109 g_mime_filter_charset_finalize (GObject *object)
111 GMimeFilterCharset *filter = (GMimeFilterCharset *) object;
113 g_free (filter->from_charset);
114 g_free (filter->to_charset);
115 if (filter->cd != (iconv_t) -1)
116 g_mime_iconv_close (filter->cd);
118 G_OBJECT_CLASS (parent_class)->finalize (object);
123 filter_copy (GMimeFilter *filter)
125 GMimeFilterCharset *charset = (GMimeFilterCharset *) filter;
127 return g_mime_filter_charset_new (charset->from_charset, charset->to_charset);
131 filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
132 char **out, size_t *outlen, size_t *outprespace)
134 GMimeFilterCharset *charset = (GMimeFilterCharset *) filter;
135 size_t inleft, outleft, converted = 0;
139 if (charset->cd == (iconv_t) -1)
142 g_mime_filter_set_size (filter, len * 5 + 16, FALSE);
143 outbuf = filter->outbuf;
144 outleft = filter->outsize;
150 converted = iconv (charset->cd, (ICONV_CONST char **) &inbuf, &inleft, (ICONV_CONST char **) &outbuf, &outleft);
151 if (converted == (size_t) -1) {
152 if (errno == E2BIG || errno == EINVAL)
155 /* Note: GnuWin32's libiconv 1.9 can also set errno to ERANGE
156 * which seems to mean that it encountered a character that
157 * does not fit the specified 'from' charset. We'll handle
158 * that the same way we handle EILSEQ. */
159 if (errno == EILSEQ || errno == ERANGE) {
161 * EILSEQ An invalid multibyte sequence has been encountered
164 * What we do here is eat the invalid bytes in the sequence
171 /* unknown error condition */
175 } while (inleft > 0);
178 /* We've either got an E2BIG or EINVAL. Save the
179 remainder of the buffer as we'll process this next
181 g_mime_filter_backup (filter, inbuf, inleft);
184 *out = filter->outbuf;
185 *outlen = outbuf - filter->outbuf;
186 *outprespace = filter->outpre;
194 *outprespace = prespace;
198 filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
199 char **out, size_t *outlen, size_t *outprespace)
201 GMimeFilterCharset *charset = (GMimeFilterCharset *) filter;
202 size_t inleft, outleft, converted = 0;
206 if (charset->cd == (iconv_t) -1)
209 g_mime_filter_set_size (filter, len * 5 + 16, FALSE);
210 outbuf = filter->outbuf;
211 outleft = filter->outsize;
218 converted = iconv (charset->cd, (ICONV_CONST char **) &inbuf, &inleft, (ICONV_CONST char **) &outbuf, &outleft);
219 if (converted != (size_t) -1)
222 if (errno == E2BIG) {
224 * E2BIG There is not sufficient room at *outbuf.
226 * We just need to grow our outbuffer and try again.
229 converted = outbuf - filter->outbuf;
230 g_mime_filter_set_size (filter, inleft * 5 + filter->outsize + 16, TRUE);
231 outbuf = filter->outbuf + converted;
232 outleft = filter->outsize - converted;
233 } else if (errno == EILSEQ) {
235 * EILSEQ An invalid multibyte sequence has been encountered
238 * What we do here is eat the invalid bytes in the sequence
244 } else if (errno == EINVAL) {
246 * EINVAL An incomplete multibyte sequence has been encounĀ
247 * tered in the input.
249 * We assume that this can only happen if we've run out of
250 * bytes for a multibyte sequence, if not we're in trouble.
257 } while (inleft > 0);
260 /* flush the iconv conversion */
261 while (iconv (charset->cd, NULL, NULL, &outbuf, &outleft) == (size_t) -1) {
265 converted = outbuf - filter->outbuf;
266 g_mime_filter_set_size (filter, filter->outsize + 16, TRUE);
267 outbuf = filter->outbuf + converted;
268 outleft = filter->outsize - converted;
271 *out = filter->outbuf;
272 *outlen = outbuf - filter->outbuf;
273 *outprespace = filter->outpre;
281 *outprespace = prespace;
285 filter_reset (GMimeFilter *filter)
287 GMimeFilterCharset *charset = (GMimeFilterCharset *) filter;
289 if (charset->cd != (iconv_t) -1)
290 iconv (charset->cd, NULL, NULL, NULL, NULL);
295 * g_mime_filter_charset_new:
296 * @from_charset: charset to convert from
297 * @to_charset: charset to convert to
299 * Creates a new #GMimeFilterCharset filter.
301 * Returns: a new charset filter or %NULL if the charset conversion is
305 g_mime_filter_charset_new (const char *from_charset, const char *to_charset)
307 GMimeFilterCharset *new;
310 cd = g_mime_iconv_open (to_charset, from_charset);
311 if (cd == (iconv_t) -1)
314 new = g_object_newv (GMIME_TYPE_FILTER_CHARSET, 0, NULL);
315 new->from_charset = g_strdup (from_charset);
316 new->to_charset = g_strdup (to_charset);
319 return (GMimeFilter *) new;