1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2004-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 /* Iconv filter: converts input between two charsets.
19
20 This filter operates in decode mode only. Initialization sequence
21 requires two arguments: the name of the input encoding and that
22 of the output encoding. Optional third argument specifies what
23 to do when an illegal character sequence is encountered on input.
24 Possible values are:
25
26 none - return failure and stop further conversions;
27 copy-pass - copy the offending character to the output verbatim;
28 copy-octal - represent it as a C octal sequence (\NNN).
29
30 The default is "copy-octal", unless overridden by mu_default_fallback_mode
31 setting (which see).
32 */
33
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
37
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <mailutils/stream.h>
43 #include <mailutils/sys/stream.h>
44 #include <mailutils/filter.h>
45 #include <mailutils/errno.h>
46 #include <mailutils/cstr.h>
47 #include <mailutils/cctype.h>
48 #include <mailutils/util.h>
49
50 #ifdef HAVE_ICONV_H
51 # include <iconv.h>
52 #endif
53
54 #ifndef ICONV_CONST
55 # define ICONV_CONST
56 #endif
57
58 #ifndef HAVE_ICONV
59 # undef iconv_open
60 # define iconv_open(tocode, fromcode) ((iconv_t) -1)
61
62 # undef iconv
63 # define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) (errno = EILSEQ, (size_t) -1)
64
65 # undef iconv_close
66 # define iconv_close(cd) 0
67
68 #endif
69
70 struct _icvt_filter
71 {
72 char *fromcode;
73 char *tocode;
74 enum mu_iconv_fallback_mode fallback_mode;
75 iconv_t cd; /* Conversion descriptor */
76 };
77
78 static void
format_octal(char * op,unsigned char n)79 format_octal (char *op, unsigned char n)
80 {
81 op += 4;
82 *--op = n % 8 + '0';
83 n >>= 3;
84 *--op = n % 8 + '0';
85 n >>= 3;
86 *--op = n % 8 + '0';
87 n >>= 3;
88 *--op = '\\';
89 }
90
91 static enum mu_filter_result
_icvt_decoder(void * xd,enum mu_filter_command cmd,struct mu_filter_io * iobuf)92 _icvt_decoder (void *xd,
93 enum mu_filter_command cmd,
94 struct mu_filter_io *iobuf)
95 {
96 struct _icvt_filter *_icvt = xd;
97 char *ip, *op;
98 size_t ilen, olen;
99 int rc;
100
101 switch (cmd)
102 {
103 case mu_filter_init:
104 if (mu_c_strcasecmp (_icvt->fromcode, _icvt->tocode) == 0)
105 _icvt->cd = (iconv_t) -1;
106 else
107 {
108 iconv_t cd = iconv_open (_icvt->tocode, _icvt->fromcode);
109 if (cd == (iconv_t) -1)
110 return mu_filter_failure;
111 _icvt->cd = cd;
112 }
113 return mu_filter_ok;
114
115 case mu_filter_done:
116 if (_icvt->cd != (iconv_t) -1)
117 iconv_close (_icvt->cd);
118 free (_icvt->fromcode);
119 free (_icvt->tocode);
120 return mu_filter_ok;
121
122 default:
123 break;
124 }
125
126 if (_icvt->cd == (iconv_t) -1)
127 {
128 size_t len = iobuf->isize;
129 if (len > iobuf->osize)
130 len = iobuf->osize;
131 memcpy (iobuf->output, iobuf->input, len);
132 iobuf->isize = len;
133 iobuf->osize = len;
134 return mu_filter_ok;
135 }
136
137 ip = (char*) iobuf->input;
138 ilen = iobuf->isize;
139 op = iobuf->output;
140 olen = iobuf->osize;
141 again:
142 rc = iconv (_icvt->cd, &ip, &ilen, &op, &olen);
143 if (rc == -1)
144 {
145 switch (errno)
146 {
147 case E2BIG:
148 iobuf->osize += 16;
149 return mu_filter_moreoutput;
150
151 case EINVAL:
152 /* An incomplete multibyte sequence is encountered in the input */
153 if (ilen == iobuf->isize)
154 {
155 iobuf->isize++;
156 return mu_filter_moreinput;
157 }
158 break;
159
160 case EILSEQ:
161 switch (_icvt->fallback_mode)
162 {
163 case mu_fallback_none:
164 iobuf->errcode = EILSEQ;
165 return mu_filter_failure;
166
167 case mu_fallback_copy_pass:
168 *op++ = *ip++;
169 ilen--;
170 olen--;
171 break;
172
173 case mu_fallback_copy_octal:
174 if (mu_isprint (*ip))
175 {
176 *op++ = *ip++;
177 ilen--;
178 olen--;
179 }
180 else
181 {
182 if (olen < 4)
183 {
184 iobuf->osize += 4;
185 return mu_filter_moreoutput;
186 }
187 format_octal (op, *(unsigned char*)ip);
188 op += 4;
189 olen -= 4;
190 ip++;
191 ilen--;
192 }
193 }
194 if (ilen && olen)
195 goto again;
196 break;
197
198 default:
199 iobuf->errcode = errno;
200 return mu_filter_failure;
201 }
202 }
203
204 iobuf->isize = ip - iobuf->input;
205 iobuf->osize = op - iobuf->output;
206 return mu_filter_ok;
207 }
208
209 static int
alloc_state(void ** pret,int mode MU_ARG_UNUSED,int argc,const char ** argv)210 alloc_state (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
211 {
212 struct _icvt_filter *flt;
213 const char *from;
214 const char *to;
215 enum mu_iconv_fallback_mode fallback_mode = mu_default_fallback_mode;
216
217 if (argc < 3)
218 return EINVAL; /* FIXME: Provide some defaults? */
219 if (argc > 4)
220 return EINVAL;
221
222 from = argv[1];
223 to = argv[2];
224
225 if (argc == 4)
226 {
227 const char *str = argv[3];
228 if (strcmp (str, "none") == 0)
229 fallback_mode = mu_fallback_none;
230 else if (strcmp (str, "copy-pass") == 0)
231 fallback_mode = mu_fallback_copy_pass;
232 else if (strcmp (str, "copy-octal") == 0)
233 fallback_mode = mu_fallback_copy_octal;
234 else
235 return EINVAL;
236 }
237
238 flt = calloc (1, sizeof (*flt));
239 if (!flt)
240 return ENOMEM;
241 flt->fromcode = strdup (from);
242 if (!flt->fromcode)
243 {
244 free (flt);
245 return ENOMEM;
246 }
247 flt->tocode = strdup (to);
248 if (!flt->tocode)
249 {
250 free (flt->fromcode);
251 free (flt);
252 return ENOMEM;
253 }
254 flt->fallback_mode = fallback_mode;
255 flt->cd = (iconv_t) -1;
256 *pret = flt;
257 return 0;
258 }
259
260 static struct _mu_filter_record _iconv_filter = {
261 "ICONV",
262 alloc_state,
263 _icvt_decoder,
264 _icvt_decoder
265 };
266
267 mu_filter_record_t mu_iconv_filter = &_iconv_filter;
268