1 /*
2 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zucchi <notzed@ximian.com>
17 * Jeffrey Stedfast <fejj@ximian.com>
18 */
19
20 #include <string.h>
21
22 #include "camel-mime-filter-basic.h"
23 #include "camel-mime-utils.h"
24
25 struct _CamelMimeFilterBasicPrivate {
26 CamelMimeFilterBasicType type;
27 guchar uubuf[60];
28 gint state;
29 gint save;
30 };
31
G_DEFINE_TYPE_WITH_PRIVATE(CamelMimeFilterBasic,camel_mime_filter_basic,CAMEL_TYPE_MIME_FILTER)32 G_DEFINE_TYPE_WITH_PRIVATE (CamelMimeFilterBasic, camel_mime_filter_basic, CAMEL_TYPE_MIME_FILTER)
33
34 /* here we do all of the basic mime filtering */
35 static void
36 mime_filter_basic_filter (CamelMimeFilter *mime_filter,
37 const gchar *in,
38 gsize len,
39 gsize prespace,
40 gchar **out,
41 gsize *outlen,
42 gsize *outprespace)
43 {
44 CamelMimeFilterBasicPrivate *priv;
45 gsize newlen;
46
47 priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
48
49 switch (priv->type) {
50 case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
51 /* wont go to more than 2x size (overly conservative) */
52 camel_mime_filter_set_size (
53 mime_filter, len * 2 + 6, FALSE);
54 newlen = g_base64_encode_step (
55 (const guchar *) in, len,
56 TRUE,
57 mime_filter->outbuf,
58 &priv->state,
59 &priv->save);
60 g_return_if_fail (newlen <= len * 2 + 6);
61 break;
62 case CAMEL_MIME_FILTER_BASIC_QP_ENC:
63 /* *4 is overly conservative, but will do */
64 camel_mime_filter_set_size (
65 mime_filter, len * 4 + 4, FALSE);
66 newlen = camel_quoted_encode_step (
67 (guchar *) in, len,
68 (guchar *) mime_filter->outbuf,
69 &priv->state,
70 (gint *) &priv->save);
71 g_return_if_fail (newlen <= len * 4 + 4);
72 break;
73 case CAMEL_MIME_FILTER_BASIC_UU_ENC:
74 /* won't go to more than 2 * (x + 2) + 62 */
75 camel_mime_filter_set_size (
76 mime_filter, (len + 2) * 2 + 62, FALSE);
77 newlen = camel_uuencode_step (
78 (guchar *) in, len,
79 (guchar *) mime_filter->outbuf,
80 priv->uubuf,
81 &priv->state,
82 (guint32 *) &priv->save);
83 g_return_if_fail (newlen <= (len + 2) * 2 + 62);
84 break;
85 case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
86 camel_mime_filter_set_size (mime_filter, (len * 3 / 4) + 3, FALSE);
87 newlen = g_base64_decode_step (
88 in, len,
89 (guchar *) mime_filter->outbuf,
90 &priv->state,
91 (guint *) &priv->save);
92 g_return_if_fail (newlen <= len + 3);
93 break;
94 case CAMEL_MIME_FILTER_BASIC_QP_DEC:
95 /* output can't possibly exceed the input size */
96 camel_mime_filter_set_size (mime_filter, len + 2, FALSE);
97 newlen = camel_quoted_decode_step (
98 (guchar *) in, len,
99 (guchar *) mime_filter->outbuf,
100 &priv->state,
101 (gint *) &priv->save);
102 g_return_if_fail (newlen <= len + 2);
103 break;
104 case CAMEL_MIME_FILTER_BASIC_UU_DEC:
105 if (!(priv->state & CAMEL_UUDECODE_STATE_BEGIN)) {
106 const gchar *inptr, *inend;
107 gsize left;
108
109 inptr = in;
110 inend = inptr + len;
111
112 while (inptr < inend) {
113 left = inend - inptr;
114 if (left < 6) {
115 if (!strncmp (inptr, "begin ", left))
116 camel_mime_filter_backup (mime_filter, inptr, left);
117 break;
118 } else if (!strncmp (inptr, "begin ", 6)) {
119 for (in = inptr; inptr < inend && *inptr != '\n'; inptr++);
120 if (inptr < inend) {
121 inptr++;
122 priv->state |= CAMEL_UUDECODE_STATE_BEGIN;
123 /* we can start uudecoding... */
124 in = inptr;
125 len = inend - in;
126 } else {
127 camel_mime_filter_backup (mime_filter, in, left);
128 }
129 break;
130 }
131
132 /* go to the next line */
133 for (; inptr < inend && *inptr != '\n'; inptr++);
134
135 if (inptr < inend)
136 inptr++;
137 }
138 }
139
140 if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) {
141 /* "begin <mode> <filename>\n" has been
142 * found, so we can now start decoding */
143 camel_mime_filter_set_size (
144 mime_filter, len + 3, FALSE);
145 newlen = camel_uudecode_step (
146 (guchar *) in, len,
147 (guchar *) mime_filter->outbuf,
148 &priv->state,
149 (guint32 *) &priv->save);
150 } else {
151 newlen = 0;
152 }
153 break;
154 default:
155 g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type);
156 goto donothing;
157 }
158
159 *out = mime_filter->outbuf;
160 *outlen = newlen;
161 *outprespace = mime_filter->outpre;
162
163 return;
164 donothing:
165 *out = (gchar *) in;
166 *outlen = len;
167 *outprespace = prespace;
168 }
169
170 static void
mime_filter_basic_complete(CamelMimeFilter * mime_filter,const gchar * in,gsize len,gsize prespace,gchar ** out,gsize * outlen,gsize * outprespace)171 mime_filter_basic_complete (CamelMimeFilter *mime_filter,
172 const gchar *in,
173 gsize len,
174 gsize prespace,
175 gchar **out,
176 gsize *outlen,
177 gsize *outprespace)
178 {
179 CamelMimeFilterBasicPrivate *priv;
180 gsize newlen = 0;
181
182 priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
183
184 switch (priv->type) {
185 case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
186 /* wont go to more than 2x size (overly conservative) */
187 camel_mime_filter_set_size (
188 mime_filter, len * 2 + 6, FALSE);
189 if (len > 0)
190 newlen += g_base64_encode_step (
191 (const guchar *) in, len,
192 TRUE,
193 mime_filter->outbuf,
194 &priv->state,
195 &priv->save);
196 newlen += g_base64_encode_close (
197 TRUE,
198 mime_filter->outbuf,
199 &priv->state,
200 &priv->save);
201 g_return_if_fail (newlen <= len * 2 + 6);
202 break;
203 case CAMEL_MIME_FILTER_BASIC_QP_ENC:
204 /* *4 is definetly more than needed ... */
205 camel_mime_filter_set_size (
206 mime_filter, len * 4 + 4, FALSE);
207 newlen = camel_quoted_encode_close (
208 (guchar *) in, len,
209 (guchar *) mime_filter->outbuf,
210 &priv->state,
211 &priv->save);
212 g_return_if_fail (newlen <= len * 4 + 4);
213 break;
214 case CAMEL_MIME_FILTER_BASIC_UU_ENC:
215 /* won't go to more than 2 * (x + 2) + 62 */
216 camel_mime_filter_set_size (
217 mime_filter, (len + 2) * 2 + 62, FALSE);
218 newlen = camel_uuencode_close (
219 (guchar *) in, len,
220 (guchar *) mime_filter->outbuf,
221 priv->uubuf,
222 &priv->state,
223 (guint32 *) &priv->save);
224 g_return_if_fail (newlen <= (len + 2) * 2 + 62);
225 break;
226 case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
227 camel_mime_filter_set_size (mime_filter, (len * 3 / 4) + 3, FALSE);
228 newlen = g_base64_decode_step (
229 in, len,
230 (guchar *) mime_filter->outbuf,
231 &priv->state,
232 (guint *) &priv->save);
233 g_return_if_fail (newlen <= len);
234 break;
235 case CAMEL_MIME_FILTER_BASIC_QP_DEC:
236 /* output can't possibly exceed the input size,
237 * well unless its not really qp, then +2 max */
238 camel_mime_filter_set_size (mime_filter, len + 2, FALSE);
239 newlen = camel_quoted_decode_step (
240 (guchar *) in, len,
241 (guchar *) mime_filter->outbuf,
242 &priv->state,
243 (gint *) &priv->save);
244 g_return_if_fail (newlen <= len + 2);
245 break;
246 case CAMEL_MIME_FILTER_BASIC_UU_DEC:
247 if ((priv->state & CAMEL_UUDECODE_STATE_BEGIN) && !(priv->state & CAMEL_UUDECODE_STATE_END)) {
248 /* "begin <mode> <filename>\n" has been
249 * found, so we can now start decoding */
250 camel_mime_filter_set_size (
251 mime_filter, len + 3, FALSE);
252 newlen = camel_uudecode_step (
253 (guchar *) in, len,
254 (guchar *) mime_filter->outbuf,
255 &priv->state,
256 (guint32 *) &priv->save);
257 } else {
258 newlen = 0;
259 }
260 break;
261 default:
262 g_warning ("unknown type %u in CamelMimeFilterBasic", priv->type);
263 goto donothing;
264 }
265
266 *out = mime_filter->outbuf;
267 *outlen = newlen;
268 *outprespace = mime_filter->outpre;
269
270 return;
271 donothing:
272 *out = (gchar *) in;
273 *outlen = len;
274 *outprespace = prespace;
275 }
276
277 /* should this 'flush' outstanding state/data bytes? */
278 static void
mime_filter_basic_reset(CamelMimeFilter * mime_filter)279 mime_filter_basic_reset (CamelMimeFilter *mime_filter)
280 {
281 CamelMimeFilterBasicPrivate *priv;
282
283 priv = CAMEL_MIME_FILTER_BASIC (mime_filter)->priv;
284
285 switch (priv->type) {
286 case CAMEL_MIME_FILTER_BASIC_QP_ENC:
287 priv->state = -1;
288 break;
289 default:
290 priv->state = 0;
291 }
292 priv->save = 0;
293 }
294
295 static void
camel_mime_filter_basic_class_init(CamelMimeFilterBasicClass * class)296 camel_mime_filter_basic_class_init (CamelMimeFilterBasicClass *class)
297 {
298 CamelMimeFilterClass *mime_filter_class;
299
300 mime_filter_class = CAMEL_MIME_FILTER_CLASS (class);
301 mime_filter_class->filter = mime_filter_basic_filter;
302 mime_filter_class->complete = mime_filter_basic_complete;
303 mime_filter_class->reset = mime_filter_basic_reset;
304 }
305
306 static void
camel_mime_filter_basic_init(CamelMimeFilterBasic * filter)307 camel_mime_filter_basic_init (CamelMimeFilterBasic *filter)
308 {
309 filter->priv = camel_mime_filter_basic_get_instance_private (filter);
310 }
311
312 /**
313 * camel_mime_filter_basic_new:
314 * @type: a #CamelMimeFilterBasicType type
315 *
316 * Create a new #CamelMimeFilterBasic object of type @type.
317 *
318 * Returns: a new #CamelMimeFilterBasic object
319 **/
320 CamelMimeFilter *
camel_mime_filter_basic_new(CamelMimeFilterBasicType type)321 camel_mime_filter_basic_new (CamelMimeFilterBasicType type)
322 {
323 CamelMimeFilter *new;
324
325 switch (type) {
326 case CAMEL_MIME_FILTER_BASIC_BASE64_ENC:
327 case CAMEL_MIME_FILTER_BASIC_QP_ENC:
328 case CAMEL_MIME_FILTER_BASIC_BASE64_DEC:
329 case CAMEL_MIME_FILTER_BASIC_QP_DEC:
330 case CAMEL_MIME_FILTER_BASIC_UU_ENC:
331 case CAMEL_MIME_FILTER_BASIC_UU_DEC:
332 new = g_object_new (CAMEL_TYPE_MIME_FILTER_BASIC, NULL);
333 CAMEL_MIME_FILTER_BASIC (new)->priv->type = type;
334 break;
335 default:
336 g_warning ("Invalid type of CamelMimeFilterBasic requested: %u", type);
337 new = NULL;
338 break;
339 }
340 camel_mime_filter_reset (new);
341
342 return new;
343 }
344
345